Demographics
Table One - Combined
neuro_pt_counts_sum <- colSums(neuro_pt_counts[,-1]) %>%
data.frame() %>%
t() %>%
data.frame()
# specify the order of Table 1
row_order_adult <- c(
"All Patients", "Female", "Male", "Unknown Sex",
"0-2", "3-5", "6-11", "12-17", "18-25",
"26-49", "50-69", "70-79", "80+", "Unknown Age",
"American Indian", "Asian", "Black",
"Hawaiian/Pacific Islander",
"Hispanic/Latino", "White", "Other",
top_comorb_adults,
"Median Elixhauser score (sd) ",
"Median pre admission cns (sd) ",
"Median pre admission pns (sd) ",
"Non-Severe", "Severe",
"Median time to severe (sd) ",
"Alive", "Deceased",
"Median time to death (sd) ",
"Discharged", "Not Discharged",
"Median time to first discharge (sd) ",
"Not Readmitted", "Readmitted",
"Median time to first readmission (sd) ",
"Median number of readmissions (sd) "
)
tableone_combine <- create_tableone(demo_table = demo_table_combine,
clinical_table = clinical_table_clean_combine) %>%
rbind(., comorb_table_wide %>%
group_by(Comorbidity) %>%
mutate(Comorb_Total = sum(Comorb_Total, na.rm = TRUE),
None_sum = sum(None_Total, na.rm = TRUE),
CNS_sum = sum(CNS_Total, na.rm = TRUE),
PNS_sum = sum(PNS_Total, na.rm = TRUE),
None_perc = round(None_sum/neuro_pt_counts_sum$None_n*100,1),
Central_perc = round(CNS_sum/neuro_pt_counts_sum$CNS_n*100,1),
Peripheral_perc = round(PNS_sum/neuro_pt_counts_sum$PNS_n*100,1),
None = paste(None_sum, "(", None_perc, ")"),
Central = paste(CNS_sum, "(", Central_perc, ")"),
Peripheral = paste(PNS_sum, "(", Peripheral_perc, ")")) %>%
distinct(Comorbidity, Comorb_Total, None, Central, Peripheral) %>%
rename(`Table 1` = "Comorbidity",
N = "Comorb_Total")) %>%
slice(match(row_order_adult, `Table 1`))
kbl(tableone_combine %>%
rename("No Neurological Condition (NNC)" = None)) %>%
add_header_above(c(
" ",
"Total Patients" = 1,
"Neurological Disease" = 3
)) %>%
kable_paper("striped", full_width = F) %>%
pack_rows("Sex", 2, 4) %>%
pack_rows("Age", 5, 13) %>%
pack_rows("Race & Ethnicity", 14, 20) %>%
pack_rows("Past Medical History", 21, 27) %>%
pack_rows("Severity", 28, 30) %>%
pack_rows("Survival", 31, 33) %>%
pack_rows("Discharge", 34, 36) %>%
pack_rows("Readmission", 37, 40)
|
|
Total Patients
|
Neurological Disease
|
|
Table 1
|
N
|
No Neurological Condition (NNC)
|
Central
|
Peripheral
|
|
All Patients
|
106229
|
88340 (100%)
|
15101 (100%)
|
2788 (100%)
|
|
Sex
|
|
Female
|
37590
|
32399 (36.7%)
|
4247 (28.1%)
|
944 (33.9%)
|
|
Male
|
68638
|
55940 (63.3%)
|
10854 (71.9%)
|
1844 (66.1%)
|
|
Unknown Sex
|
1
|
1 (0%)
|
0 (0%)
|
0 (0%)
|
|
Age
|
|
0-2
|
769
|
747 (0.8%)
|
22 (0.1%)
|
0 (0%)
|
|
3-5
|
275
|
251 (0.3%)
|
24 (0.2%)
|
0 (0%)
|
|
6-11
|
403
|
368 (0.4%)
|
33 (0.2%)
|
2 (0.1%)
|
|
12-17
|
707
|
637 (0.7%)
|
60 (0.4%)
|
10 (0.4%)
|
|
18-25
|
2328
|
2138 (2.4%)
|
145 (1%)
|
45 (1.6%)
|
|
26-49
|
17799
|
16122 (18.3%)
|
1166 (7.7%)
|
511 (18.5%)
|
|
50-69
|
36885
|
31556 (35.7%)
|
4169 (27.7%)
|
1160 (41.9%)
|
|
70-79
|
24860
|
19672 (22.3%)
|
4508 (30%)
|
680 (24.6%)
|
|
80+
|
22094
|
16813 (19%)
|
4920 (32.7%)
|
361 (13%)
|
|
Race & Ethnicity
|
|
American Indian
|
350
|
295 (0.3%)
|
55 (0.4%)
|
0 (0%)
|
|
Asian
|
1694
|
1464 (1.7%)
|
205 (1.4%)
|
25 (0.9%)
|
|
Black
|
16815
|
13368 (15.1%)
|
2995 (19.9%)
|
452 (16.5%)
|
|
Hawaiian/Pacific Islander
|
297
|
255 (0.3%)
|
41 (0.3%)
|
1 (0%)
|
|
Hispanic/Latino
|
870
|
819 (0.9%)
|
29 (0.2%)
|
22 (0.8%)
|
|
White
|
41871
|
33360 (37.8%)
|
7401 (49.1%)
|
1110 (40.5%)
|
|
Other
|
44220
|
38750 (43.9%)
|
4336 (28.8%)
|
1134 (41.3%)
|
|
Past Medical History
|
|
Hypertension
|
35587
|
27323 ( 30.9 )
|
7277 ( 48.2 )
|
987 ( 35.4 )
|
|
Alcohol abuse
|
30130
|
23065 ( 26.1 )
|
6194 ( 41 )
|
871 ( 31.2 )
|
|
Drug abuse
|
26596
|
20287 ( 23 )
|
5528 ( 36.6 )
|
781 ( 28 )
|
|
Diabetes
|
21969
|
16905 ( 19.1 )
|
4429 ( 29.3 )
|
635 ( 22.8 )
|
|
Median Elixhauser score (sd)
|
0.3 (0.7)
|
0.2 (0.5)
|
1.4 (2)
|
0.6 (1.7)
|
|
Median pre admission cns (sd)
|
0 (0)
|
0 (0)
|
0 (0)
|
0 (0)
|
|
Median pre admission pns (sd)
|
0 (0)
|
0 (0)
|
0 (0)
|
0 (0.1)
|
|
Severity
|
|
Non-Severe
|
66252
|
57771 (65.4%)
|
7007 (46.4%)
|
1474 (52.7%)
|
|
Severe
|
39985
|
30576 (34.6%)
|
8084 (53.6%)
|
1325 (47.3%)
|
|
Median time to severe (sd)
|
0.6 (1)
|
0.8 (1.3)
|
0.4 (0.7)
|
0.8 (1.1)
|
|
Survival
|
|
Alive
|
87376
|
74389 (84.2%)
|
10392 (68.8%)
|
2595 (93.1%)
|
|
Deceased
|
18849
|
13953 (15.8%)
|
4705 (31.2%)
|
191 (6.9%)
|
|
Median time to death (sd)
|
15.3 (6.4)
|
15.9 (5.8)
|
22.4 (32.5)
|
36.9 (41.4)
|
|
Discharge
|
|
Discharged
|
103165
|
85639 (96.9%)
|
14801 (98%)
|
2725 (97.7%)
|
|
Not Discharged
|
3063
|
2701 (3.1%)
|
297 (2%)
|
65 (2.3%)
|
|
Median time to first discharge (sd)
|
8.4 (4.2)
|
6.5 (3.6)
|
12.8 (6.1)
|
14.5 (18.3)
|
|
Readmission
|
|
Not Readmitted
|
91646
|
76877 (87%)
|
12435 (82.4%)
|
2334 (83.4%)
|
|
Readmitted
|
14569
|
11456 (13%)
|
2650 (17.6%)
|
463 (16.6%)
|
|
Median time to first readmission (sd)
|
28.1 (20.3)
|
26.4 (17.6)
|
31.9 (33.7)
|
38.1 (28.4)
|
|
Median number of readmissions (sd)
|
0.1 (0.3)
|
0 (0)
|
0.1 (0.3)
|
0.1 (0.3)
|
write.csv(tableone_combine, 'tables/table1.csv', row.names = FALSE)
Table One - Adults
tableone_adult <- create_tableone(demo_table = demo_table_adult,
clinical_table = clinical_table_adult_clean) %>%
rbind(., comorbidity_table_adults %>%
select(Comorbidity, Comorb_Total, `NNC_N_%`, `CNS_N_%`, `PNS_N_%`) %>%
rename(`Table 1` = "Comorbidity",
N = "Comorb_Total",
None = "NNC_N_%",
Central = "CNS_N_%",
Peripheral = "PNS_N_%")) %>%
slice(match(row_order_adult, `Table 1`))
kbl(tableone_adult %>%
rename("No Neurological Condition (NNC)" = None)) %>%
add_header_above(c(
" ",
"Total Patients" = 1,
"Neurological Disease" = 3
)) %>%
kable_paper("striped", full_width = F) %>%
pack_rows("Sex", 2, 4) %>%
pack_rows("Age", 5, 9) %>%
pack_rows("Race & Ethnicity", 10, 16) %>%
pack_rows("Past Medical History", 17, 23) %>%
pack_rows("Severity", 24, 26) %>%
pack_rows("Survival", 27, 29) %>%
pack_rows("Discharge", 30, 32) %>%
pack_rows("Readmission", 33, 36)
|
|
Total Patients
|
Neurological Disease
|
|
Table 1
|
N
|
No Neurological Condition (NNC)
|
Central
|
Peripheral
|
|
All Patients
|
104031
|
86321 (100%)
|
14938 (100%)
|
2772 (100%)
|
|
Sex
|
|
Female
|
36564
|
31445 (36.4%)
|
4185 (28%)
|
934 (33.7%)
|
|
Male
|
67466
|
54875 (63.6%)
|
10753 (72%)
|
1838 (66.3%)
|
|
Unknown Sex
|
1
|
1 (0%)
|
0 (0%)
|
0 (0%)
|
|
Age
|
|
18-25
|
2328
|
2138 (2.5%)
|
145 (1%)
|
45 (1.6%)
|
|
26-49
|
17799
|
16122 (18.7%)
|
1166 (7.8%)
|
511 (18.5%)
|
|
50-69
|
36885
|
31556 (36.6%)
|
4169 (28%)
|
1160 (42.1%)
|
|
70-79
|
24860
|
19672 (22.8%)
|
4508 (30.2%)
|
680 (24.7%)
|
|
80+
|
22094
|
16813 (19.5%)
|
4920 (33%)
|
361 (13.1%)
|
|
Race & Ethnicity
|
|
American Indian
|
349
|
294 (0.3%)
|
55 (0.4%)
|
0 (0%)
|
|
Asian
|
1633
|
1412 (1.6%)
|
196 (1.3%)
|
25 (0.9%)
|
|
Black
|
16631
|
13202 (15.3%)
|
2979 (20%)
|
450 (16.5%)
|
|
Hawaiian/Pacific Islander
|
296
|
254 (0.3%)
|
41 (0.3%)
|
1 (0%)
|
|
Hispanic/Latino
|
850
|
799 (0.9%)
|
29 (0.2%)
|
22 (0.8%)
|
|
White
|
41206
|
32752 (38%)
|
7354 (49.3%)
|
1100 (40.3%)
|
|
Other
|
42971
|
37587 (43.6%)
|
4254 (28.5%)
|
1130 (41.4%)
|
|
Past Medical History
|
|
Hypertension
|
35566
|
27302 ( 31.6 %)
|
7277 ( 48.7 %)
|
987 ( 35.6 %)
|
|
Alcohol abuse
|
29918
|
22879 ( 26.5 %)
|
6169 ( 41.3 %)
|
870 ( 31.4 %)
|
|
Drug abuse
|
26389
|
20097 ( 23.3 %)
|
5512 ( 36.9 %)
|
780 ( 28.1 %)
|
|
Diabetes
|
21952
|
16888 ( 19.6 %)
|
4429 ( 29.6 %)
|
635 ( 22.9 %)
|
|
Median Elixhauser score (sd)
|
0.3 (0.7)
|
0.2 (0.5)
|
1.4 (2)
|
0.6 (1.7)
|
|
Median pre admission cns (sd)
|
0 (0)
|
0 (0)
|
0 (0)
|
0 (0)
|
|
Median pre admission pns (sd)
|
0 (0)
|
0 (0)
|
0 (0)
|
0 (0.1)
|
|
Severity
|
|
Non-Severe
|
64578
|
56181 (65.1%)
|
6935 (46.4%)
|
1462 (52.6%)
|
|
Severe
|
39468
|
30140 (34.9%)
|
8008 (53.6%)
|
1320 (47.4%)
|
|
Median time to severe (sd)
|
0.6 (1)
|
0.8 (1.3)
|
0.4 (0.7)
|
0.8 (1.1)
|
|
Survival
|
|
Alive
|
85212
|
72388 (83.9%)
|
10247 (68.6%)
|
2577 (93.1%)
|
|
Deceased
|
18820
|
13933 (16.1%)
|
4696 (31.4%)
|
191 (6.9%)
|
|
Median time to death (sd)
|
15.3 (6.4)
|
15.9 (5.8)
|
22.4 (32.5)
|
36.9 (41.4)
|
|
Discharge
|
|
Discharged
|
101032
|
83675 (96.9%)
|
14650 (98%)
|
2707 (97.7%)
|
|
Not Discharged
|
3005
|
2645 (3.1%)
|
295 (2%)
|
65 (2.3%)
|
|
Median time to first discharge (sd)
|
8.4 (4.2)
|
6.5 (3.6)
|
12.8 (6.1)
|
14.5 (18.3)
|
|
Readmission
|
|
Not Readmitted
|
89802
|
75170 (87.1%)
|
12312 (82.4%)
|
2320 (83.5%)
|
|
Readmitted
|
14237
|
11151 (12.9%)
|
2626 (17.6%)
|
460 (16.5%)
|
|
Median time to first readmission (sd)
|
28.1 (20.3)
|
26.4 (17.6)
|
31.9 (33.7)
|
38.1 (28.4)
|
|
Median number of readmissions (sd)
|
0.1 (0.3)
|
0 (0)
|
0.1 (0.3)
|
0.1 (0.3)
|
Table One - Pediatrics
# specify the order of Table 1
row_order_pediatric <- c(
"All Patients", "Female", "Male", "Unknown Sex",
"0-2", "3-5", "6-11", "12-17", "18-25",
"26-49", "50-69", "70-79", "80+", "Unknown Age",
"American Indian", "Asian", "Black",
"Hawaiian/Pacific Islander",
"Hispanic/Latino", "White", "Other",
top_comorb_pediatrics,
"Median Elixhauser score (sd) ",
"Median pre admission cns (sd) ",
"Median pre admission pns (sd) ",
"Non-Severe", "Severe",
"Median time to severe (sd) ",
"Alive", "Deceased",
"Median time to death (sd) ",
"Discharged", "Not Discharged",
"Median time to first discharge (sd) ",
"Not Readmitted", "Readmitted",
"Median time to first readmission (sd) ",
"Median number of readmissions (sd) "
)
tableone_pediatric <- create_tableone(demo_table = demo_table_pediatric,
clinical_table = clinical_table_pediatric_clean) %>%
rbind(., comorbidity_table_pediatric %>%
select(Comorbidity, Comorb_Total, `NNC_N_%`, `CNS_N_%`, `PNS_N_%`) %>%
rename(`Table 1` = "Comorbidity",
N = "Comorb_Total",
None = "NNC_N_%",
Central = "CNS_N_%",
Peripheral = "PNS_N_%")) %>%
slice(match(row_order_pediatric, `Table 1`))
kbl(tableone_pediatric %>%
rename("No Neurological Condition (NNC)" = None)) %>%
add_header_above(c(
" ",
"Total Patients" = 1,
"Neurological Disease" = 3
)) %>%
kable_paper("striped", full_width = F) %>%
pack_rows("Sex", 2, 4) %>%
pack_rows("Age", 5, 8) %>%
pack_rows("Race & Ethnicity", 9, 15) %>%
pack_rows("Past Medical History", 16, 22) %>%
pack_rows("Severity", 23, 25) %>%
pack_rows("Survival", 26, 28) %>%
pack_rows("Discharge", 29, 31) %>%
pack_rows("Readmission", 32, 35)
|
|
Total Patients
|
Neurological Disease
|
|
Table 1
|
N
|
No Neurological Condition (NNC)
|
Central
|
Peripheral
|
|
All Patients
|
2198
|
2019 (100%)
|
163 (100%)
|
16 (100%)
|
|
Sex
|
|
Female
|
1026
|
954 (47.3%)
|
62 (38%)
|
10 (62.5%)
|
|
Male
|
1172
|
1065 (52.7%)
|
101 (62%)
|
6 (37.5%)
|
|
Unknown Sex
|
0
|
0 (0%)
|
0 (0%)
|
0 (0%)
|
|
Age
|
|
0-2
|
769
|
747 (37.3%)
|
22 (15.8%)
|
0 (0%)
|
|
3-5
|
275
|
251 (12.5%)
|
24 (17.3%)
|
0 (0%)
|
|
6-11
|
403
|
368 (18.4%)
|
33 (23.7%)
|
2 (16.7%)
|
|
12-17
|
707
|
637 (31.8%)
|
60 (43.2%)
|
10 (83.3%)
|
|
Race & Ethnicity
|
|
American Indian
|
1
|
1 (0%)
|
0 (0%)
|
0 (0%)
|
|
Asian
|
61
|
52 (2.6%)
|
9 (5.8%)
|
0 (0%)
|
|
Black
|
184
|
166 (8.3%)
|
16 (10.4%)
|
2 (12.5%)
|
|
Hawaiian/Pacific Islander
|
1
|
1 (0%)
|
0 (0%)
|
0 (0%)
|
|
Hispanic/Latino
|
20
|
20 (1%)
|
0 (0%)
|
0 (0%)
|
|
White
|
665
|
608 (30.2%)
|
47 (30.5%)
|
10 (62.5%)
|
|
Other
|
1249
|
1163 (57.8%)
|
82 (53.2%)
|
4 (25%)
|
|
Past Medical History
|
|
Alcohol abuse
|
212
|
186 ( 9.2 %)
|
25 ( 15.3 %)
|
1 ( 6.2 %)
|
|
Drug abuse
|
207
|
190 ( 9.4 %)
|
16 ( 9.8 %)
|
1 ( 6.2 %)
|
|
Weight loss
|
123
|
109 ( 5.4 %)
|
13 ( 8 %)
|
1 ( 6.2 %)
|
|
Cardiac arrhythmias
|
109
|
94 ( 4.7 %)
|
12 ( 7.4 %)
|
3 ( 18.8 %)
|
|
Median Elixhauser score (sd)
|
2.2 (4.5)
|
0 (0)
|
4.4 (9.2)
|
2.5 (3.6)
|
|
Median pre admission cns (sd)
|
0.2 (0.7)
|
0.1 (0.2)
|
1.9 (3.9)
|
0 (0)
|
|
Median pre admission pns (sd)
|
0 (0)
|
0 (0)
|
0 (0)
|
0.2 (0.6)
|
|
Severity
|
|
Non-Severe
|
1674
|
1590 (78.5%)
|
72 (48.6%)
|
12 (70.6%)
|
|
Severe
|
517
|
436 (21.5%)
|
76 (51.4%)
|
5 (29.4%)
|
|
Median time to severe (sd)
|
2.4 (5.3)
|
3.8 (9.5)
|
10.8 (33.1)
|
0.9 (2.5)
|
|
Survival
|
|
Alive
|
2164
|
2001 (99%)
|
145 (94.2%)
|
18 (100%)
|
|
Deceased
|
29
|
20 (1%)
|
9 (5.8%)
|
0 (0%)
|
|
Median time to death (sd)
|
12.6 (36.3)
|
53.5 (76.3)
|
5.8 (10.2)
|
95 (268.7)
|
|
Discharge
|
|
Discharged
|
2133
|
1964 (97.2%)
|
151 (98.7%)
|
18 (100%)
|
|
Not Discharged
|
58
|
56 (2.8%)
|
2 (1.3%)
|
0 (0%)
|
|
Median time to first discharge (sd)
|
5.1 (4.2)
|
3.8 (4.2)
|
5.8 (2.7)
|
5.9 (4.5)
|
|
Readmission
|
|
Not Readmitted
|
1844
|
1707 (84.8%)
|
123 (83.7%)
|
14 (82.4%)
|
|
Readmitted
|
332
|
305 (15.2%)
|
24 (16.3%)
|
3 (17.6%)
|
|
Median time to first readmission (sd)
|
20 (20.2)
|
20.1 (19)
|
75.3 (86)
|
14.4 (22.8)
|
|
Median number of readmissions (sd)
|
0.1 (0.3)
|
0 (0)
|
0.2 (0.4)
|
0.4 (0.5)
|
Sample Size
n_adult <- as.numeric(tableone_adult$N)[1]
n_pediatric <- as.numeric(tableone_pediatric$N)[1]
Demographic & Clinical Course Analysis
Evaluate characteristics among neuro status groups
1. Conduct chi-squared tests for categorical demographic
variables
compute_chi_square <- function(tableone, demo_table) {
N = tableone %>% select(N) %>% head(1) %>% as.numeric()
# sum of all neuro condition groups
cns_n = as.numeric(gsub( " .*$", "", tableone[1,"Central"]))
pns_n = as.numeric(gsub( " .*$", "", tableone[1,"Peripheral"]))
none_n = as.numeric(gsub( " .*$", "", tableone[1,"None"]))
# sum up the total counts of each demographic variable across healthcare systems
tableOne_sums <- demo_table %>%
group_by(variable, Demo_var, Demo_var_i) %>%
summarise(across(
starts_with("n_var"),
function(x) sum(x, na.rm = TRUE)
), .groups = "drop")
# create list of demographic/clinical variables
vars = tableOne_sums$variable
# for binary variables, we should group by category
# Thus, only run the analysis for sex.male rather than both sex.female AND sex.male which would be redundant
# we will also remove `age_group.unknown` since their is only 1 patient here
exclude <- c("readmitted.false", "sex.female", "sex.other", "survival.deceased", "severity.non-severe",
"covid_discharged.discharged", "age_group.Unknown")
vars <- vars[!vars %in% exclude]
# create empty list for chi.square test results
chi_result_list = list()
# for each variable, we will run the chi.square test
for(i in vars) {
test = calc_chisq(i, tableOne_sums, none_n, pns_n, cns_n)
X2 <- round(test$statistic, 4)
p_value <- test$p.value
df <- test$parameter
Variable = paste(i)
chi_results <- cbind(Variable, X2, p_value, df) %>%
data.frame() %>%
mutate(p_value = as.numeric(p_value))
rownames(chi_results) <- NULL
chi_result_list[[i]] <- chi_results
}
# save list of chi.square test results
chisq_results <- bind_rows(chi_result_list)
return(chisq_results)
}
Combined Adult & Pediatric
chisq_combine <- compute_chi_square(tableone = tableone_combine %>%
filter(!`Table 1` == 'Discharged',
!`Table 1` == 'Not Discharged'),
demo_table = demo_table_combine %>%
filter(!variable == 'covid_discharged.discharged',
!variable == 'covid_discharged.not discharged'))
datatable(chisq_combine %>% arrange(p_value))
Adult
chisq_adult <- compute_chi_square(tableone = tableone_adult %>%
filter(!`Table 1` == 'Discharged',
!`Table 1` == 'Not Discharged'),
demo_table = demo_table_adult %>%
filter(!variable == 'covid_discharged.discharged',
!variable == 'covid_discharged.not discharged'))
datatable(chisq_adult %>% arrange(p_value))
Pediatric
chisq_pediatric <- compute_chi_square(tableone = tableone_pediatric %>%
filter(!`Table 1` == 'Discharged',
!`Table 1` == 'Not Discharged'),
demo_table = demo_table_pediatric %>%
filter(!variable == 'covid_discharged.discharged',
!variable == 'covid_discharged.not discharged'))
## Warning in chisq.test(unlist(mat)): Chi-squared approximation may be incorrect
## Warning in chisq.test(unlist(mat)): Chi-squared approximation may be incorrect
## Warning in chisq.test(unlist(mat)): Chi-squared approximation may be incorrect
## Warning in chisq.test(unlist(mat)): Chi-squared approximation may be incorrect
## Warning in chisq.test(unlist(mat)): Chi-squared approximation may be incorrect
## Warning in chisq.test(unlist(mat)): Chi-squared approximation may be incorrect
## Warning in chisq.test(unlist(mat)): Chi-squared approximation may be incorrect
## Warning in chisq.test(unlist(mat)): Chi-squared approximation may be incorrect
## Warning in chisq.test(unlist(mat)): Chi-squared approximation may be incorrect
## Warning in chisq.test(unlist(mat)): Chi-squared approximation may be incorrect
## Warning in chisq.test(unlist(mat)): Chi-squared approximation may be incorrect
datatable(chisq_pediatric %>% arrange(p_value))
2. Conduct anova (Kruskal-Wallis) tests for continuous
clinical variables
compute_kruskal <- function(clinical_table) {
# create empty list to save processed variables
processed_list_figs <- list()
# create a list of clinical variables to further pre-process
vars_to_process <- unique(clinical_table$name)
# for each variable, we will remove the [min, max] as before
for (i in vars_to_process) {
mod_table <- clinical_table %>%
rename("var" = name) %>%
filter(var == i) %>%
mutate(
site = toupper(site),
None = sub("(\\(.*|\\[.*)", "", None),
Peripheral = sub("(\\(.*|\\[.*)", "", Peripheral),
Central = sub("(\\(.*|\\[.*)", "", Central)
) %>%
mutate(across(None:Central, as.numeric))
processed_list_figs[[i]] <- mod_table
}
clinical_table_anova <- bind_rows(processed_list_figs)
# format the continuous variables
cont_vars = clinical_table_anova %>%
mutate(
# create a variable without mean/median prefix
grouped_var = gsub("Mean |Median | \\[Min, Max\\]| \\(SD\\)", "", var)
) %>%
pivot_longer(
cols = None:Central,
names_to = "type"
) %>%
mutate(type = factor(type, levels = c("None", "Central", "Peripheral"))) %>%
# select variables to analyze
filter(grouped_var %in% c("Elixhauser score",
"pre admission cns",
"pre admission pns",
"time to death",
"time to first discharge",
"time to severe",
"number of readmissions",
"time to first readmission")) %>%
distinct()
# create function to conduct anova
cont_var_results <- function(variable) {
df <- cont_vars %>%
filter(grouped_var == paste(variable))
# kruskal.test calculates the kruskal-Wallis H-statistic
# does not assume normality between groups
# also called the one-way ANOVA on ranks
test = kruskal.test(df$value ~ df$type)
}
# create empty list for Kruskal-Wallis results
cont_vars_list = list()
# create list of unique variables
outcome_cont_vars = unique(cont_vars$grouped_var)
for(i in outcome_cont_vars) {
test = cont_var_results(i)
X2 <- round(test$statistic, 4)
p_value <- test$p.value
df <- test$parameter
Variable = paste(i)
kw <- cbind(Variable, X2, p_value, df) %>%
data.frame()
rownames(kw) <- NULL
cont_vars_list[[i]] <- kw
}
anova_results <- bind_rows(cont_vars_list)
}
Adult & Pediatric Kruskall-Wallis
kw_combine <- compute_kruskal(clinical_table = clinical_table_combine)
datatable(kw_combine %>% arrange(p_value))
Adult Clinical Kruskall-Wallis
Note: median CNS is 0 for all sites (reason for the NA
values)
kw_adult <- compute_kruskal(clinical_table = clinical_table_adult)
datatable(kw_adult %>% arrange(p_value))
Pediatric Kruskall-Wallis
kw_pediatric <- compute_kruskal(clinical_table = clinical_table_pediatric)
datatable(kw_pediatric %>% arrange(p_value))
3. Combine chi-square and Kruskal-Wallis results
We will evaluate significance when controlling for False Discovery
Rate (FDR)
tableone_stats <- function(chisq_results, anova_results, population) {
demo_stats <- rbind(chisq_results %>% select(Variable, p_value),
anova_results %>% select(Variable, p_value))
# adjust p-values with FDR
demo_stats$adj_p_value <- p.adjust(demo_stats$p_value, method = "fdr")
# round & format p-value
demo_stats <- demo_stats %>%
mutate(p_value = as.numeric(p_value),
pvalue = if_else(p_value < 0.001, "< 0.001", paste(round(p_value, 3))),
adj.p_value = if_else(adj_p_value < 0.001, "< 0.001", paste(round(adj_p_value, 3))))
# tidy up the demographics stat table
demo_stats_tidy <- demo_stats %>%
rename(`Unadjusted P-value (raw)` = p_value,
`FDR Adjusted P-value (raw)` = adj_p_value,
`Unadjusted P-value (tidy)` = pvalue,
`FDR Adjusted P-value (tidy)` = adj.p_value) %>%
select(Variable, `Unadjusted P-value (raw)`, `FDR Adjusted P-value (raw)`, `Unadjusted P-value (tidy)`, `FDR Adjusted P-value (tidy)`)
write.csv(demo_stats_tidy, paste0("tables/Table1_pvals_", population, ".csv"), row.names = FALSE)
return(demo_stats_tidy)
}
Incorporate Comorbidities
# define matrix for each comorbidity
comorb_matrix <- function(comorbidity_table, comorbidity, population) {
if(population=='Combined') {
comorbidity_table = comorbidity_table
} else if (population=="Adult") {
comorbidity_table = comorbidity_table %>%
filter(population == "Adult")
} else if (population=="Pediatric") {
comorbidity_table = comorbidity_table %>%
filter(population == "Pediatric")
} else {
print('incorrect population specification')
}
mat_comorb <- comorbidity_table %>%
filter(Comorbidity == paste(comorbidity)) %>%
group_by(Comorbidity) %>%
mutate(None_comorb = sum(None_Total, na.rm = TRUE),
CNS_comorb = sum(CNS_Total, na.rm = TRUE),
PNS_comorb = sum(PNS_Total, na.rm = TRUE)) %>%
ungroup() %>%
distinct(None_comorb, CNS_comorb, PNS_comorb) %>%
mutate(none_dif = neuro_pt_counts_sum$None_n - None_comorb,
pns_dif = neuro_pt_counts_sum$PNS_n - PNS_comorb,
cns_dif = neuro_pt_counts_sum$CNS_n - CNS_comorb) %>%
data.frame() %>%
#as.integer() %>%
matrix(nrow = 2, ncol = 3, byrow = TRUE)
comorb_results <- chisq.test(unlist(mat_comorb))
results_df <- data.frame(Variable = paste(comorbidity),
X2 = round(comorb_results$statistic, 4),
p_value = comorb_results$p.value,
df = comorb_results$parameter)
row.names(results_df) <- NULL
return(results_df)
}
## combined comorbidities
comorbidity_combined_chi_list <- list()
for(i in top_comorb_adults) {
comorb_chi_results <- comorb_matrix(comorbidity_table = comorb_table_wide, i, population = "Combined")
comorbidity_combined_chi_list[[i]] <- comorb_chi_results
}
comorbidity_combined_chi <- comorbidity_combined_chi_list %>%
bind_rows()
chisq_combine_comorb <- chisq_combine %>%
mutate(X2 = as.numeric(X2),
df = as.numeric(df)) %>%
bind_rows(comorbidity_combined_chi)
## adult comorbidities
comorbidity_adult_chi_list <- list()
for(i in top_comorb_adults) {
comorb_chi_results <- comorb_matrix(comorbidity_table = comorb_table_wide, i, population = "Adult")
comorbidity_adult_chi_list[[i]] <- comorb_chi_results
}
comorbidity_adult_chi <- comorbidity_adult_chi_list %>%
bind_rows()
chisq_adult_comorb <- chisq_adult %>%
mutate(X2 = as.numeric(X2),
df = as.numeric(df)) %>%
bind_rows(comorbidity_adult_chi)
## pediatric comorbidities
comorbidity_pediatric_chi_list <- list()
for(i in top_comorb_pediatrics) {
comorb_chi_results <- comorb_matrix(comorbidity_table = comorb_table_wide, i, population = "Pediatric")
comorbidity_pediatric_chi_list[[i]] <- comorb_chi_results
}
comorbidity_pediatric_chi <- comorbidity_pediatric_chi_list %>%
bind_rows()
chisq_pediatric_comorb <- chisq_pediatric %>%
mutate(X2 = as.numeric(X2),
df = as.numeric(df)) %>%
bind_rows(comorbidity_pediatric_chi_list)
Table One - Combined Analysis
combine_tableone_stats <- tableone_stats(chisq_results = chisq_combine_comorb,
anova_results = kw_combine,
population = "combined")
datatable(combine_tableone_stats)
Table One - Adult Analysis
adult_tableone_stats <- tableone_stats(chisq_results = chisq_adult_comorb,
anova_results = kw_adult,
population = "adult")
datatable(adult_tableone_stats)
Table One - Pediatric Analysis
pediatric_tableone_stats <- tableone_stats(chisq_results = chisq_pediatric_comorb,
anova_results = kw_pediatric,
population = "pediatric")
datatable(pediatric_tableone_stats)
Neurological Diagnosis Analysis
Evaluate CNS and PNS neurological diagnoses in our patient cohort
Format Diagnostic code data
# import list of ICD-9/ICD-10 codes
neuro_icds_10 <-
readxl::read_excel("public-data/2020-02-22_neuro-icd10_CNSvPNS.xlsx", sheet = 2) %>%
rename(
"icd" = `ICD-10`,
"pns_cns" = `Nervous system Involvement (1=central, 2=peripheral, 3=irrelevant)`,
"type" = `ICD-10_type (1=first three alphanumeric code only, 2=digits after decimal point)`) %>%
filter(type == 1) %>%
mutate(pns_cns = as.factor(pns_cns) %>% fct_recode(
Central = "1",
Peripheral = "2"
)) %>%
distinct(icd, `Neurological Disease Category`, pns_cns, `ICD-10 Description Revised`) %>%
rename("description" = `ICD-10 Description Revised`) %>%
mutate(concept_type = "icd-10")
neuro_icds_9 <- read.csv("public-data/icd9_tab_CNSvPNS.csv") %>%
rename(
"Neurological Disease Category" = "Neurological.Disease.Category",
"pns_cns" = `Nervous.system.Involvement..1.central..2.peripheral.`,
"icd_description" = `icd9_desc_revised`
) %>%
mutate(
pns_cns = as.factor(pns_cns) %>% fct_recode(
Central = "1",
Peripheral = "2"
),
concept_type = "DIAG-ICD9"
) %>%
select(icd, `Neurological Disease Category`, pns_cns, icd_description) %>%
rename("description" = icd_description) %>%
mutate(concept_type = "icd-9")
# bind list of ICD-9/ICD-10 codes
icds <- rbind(neuro_icds_10, neuro_icds_9)
Calculate total number of neuro diagnoses for each adults
& pediatrics
compute_neuro_counts <- function(sorted_sites, sample_size, is_pediatric = FALSE) {
# create empty list to save processed diagnostic data
diag_table_list <- list()
if(is_pediatric==FALSE) {
population = "adults"
} else {
population = "pediatrics"
}
# for each healthcare system, we will calculate the total number of patients with each ICD code
for (i in sorted_sites) {
tmp <- get(i)
tmp_diag <- tmp[[c(
"first_hosp_results",
"icd_tables",
paste0("icd_tables_", population),
"demo_table"
)]]
tmp_diag$site <- tmp[["site"]]
diag_table_list[[i]] <- tmp_diag %>%
filter(variable == "sex.female" | variable == "sex.male" | variable == "sex.other") %>%
select(site, contains("n_var"))
}
diag_table_wide <- bind_rows(diag_table_list)
# we will count the total number and percent across healthcare systems
diag_counts <- colSums(Filter(is.numeric, diag_table_wide), na.rm = TRUE) %>%
data.frame() %>%
mutate(perc = round(./sample_size * 100, 1))
# format diagnostic table
diag_counts <- tibble::rownames_to_column(diag_counts, "icd") %>%
mutate(icd = gsub("n_var_", "", icd)) %>%
rename(`Number of Patients` = ".") %>%
full_join(., icds, by = "icd") %>%
dplyr::arrange(desc(`Number of Patients`)) %>%
mutate(`Number of Patients (% of Cohort)` = paste(`Number of Patients`, "(", perc, ")", sep = " ")) %>%
select(pns_cns, `Neurological Disease Category`, concept_type,
icd, description, `Number of Patients (% of Cohort)`) %>%
rename(Code = "icd",
`Nervous System` = "pns_cns",
Description = "description",
`ICD Version` = "concept_type") %>%
mutate(`Number of Patients (% of Cohort)` = if_else(`Number of Patients (% of Cohort)` == "NA ( NA )", "0 ( 0 )", `Number of Patients (% of Cohort)`),
Code = if_else(Code == "NN", "No Neurological Disease (NNC)", Code))
return(diag_counts)
}
neuro_counts_adult <- compute_neuro_counts(sorted_sites = adult_sites,
sample_size = n_adult,
is_pediatric = FALSE)
neuro_counts_pediatric <- compute_neuro_counts(sorted_sites = pediatric_sites,
sample_size = n_pediatric,
is_pediatric = TRUE)
neuro_counts_combined <- neuro_counts_adult %>%
rename(`Number of Adult Patients (% of Cohort)` = `Number of Patients (% of Cohort)`) %>%
left_join(., neuro_counts_pediatric %>%
select(`ICD Version`, Code, `Number of Patients (% of Cohort)`) %>%
rename("Number of Pediatric Patients (% of Cohort`)" = `Number of Patients (% of Cohort)`))
## Joining, by = c("ICD Version", "Code")
write.csv(neuro_counts_combined, paste0("tables/Supp.Table2_diagnoses", ".csv"), row.names = FALSE)
datatable(neuro_counts_adult, caption = "Number (%) of Adult Patients with each Neurological Diagnosis")
datatable(neuro_counts_pediatric, caption = "Number (%) of Pediatric Patients with each Neurological Diagnosis")
Evaluate Diagnoses by Age & Outcome
compute_stratified_neuro_counts <- function(tableone, sorted_sites, is_pediatric = FALSE) {
# create empty list to save processed diagnostic data
diag_table_list <- list()
if(is_pediatric==FALSE) {
population = "adults"
} else {
population = "pediatrics"
}
# for each healthcare system, we will calculate the total number of patients with each ICD code
for (i in sorted_sites) {
#print(i)
tmp <- get(i)
tmp_diag <- tmp[[c(
"first_hosp_results",
"icd_tables",
paste0("icd_tables_", population),
"demo_table"
)]]
tmp_diag$site <- tmp[["site"]]
try(
diag_table_list[[i]] <- tmp_diag %>%
select(site, variable, starts_with("n_var")) %>%
pivot_longer(cols = starts_with("n_var"), names_to = "code", values_to = "freq") %>%
ungroup()
)
}
diag_table_wide_all <- bind_rows(diag_table_list) %>%
group_by(variable, code) %>%
# total number of patients who had each code for each variable across sites
mutate(total_n_var = sum(freq, na.rm = TRUE)) %>%
distinct(variable, code, total_n_var) %>%
mutate(code = gsub("n_var_", "", code),
variable = gsub("Severity.", "", variable),
variable = gsub("Survival.", "", variable),
variable = gsub("readmitted", "", variable),
variable = gsub("age_group", "", variable),
variable = gsub("covid_discharged", "", variable),
variable = if_else(variable == ".TRUE", "Readmitted", variable)) %>%
left_join(., icds %>% rename(code = "icd"), by = "code") %>%
ungroup() %>%
filter(!concept_type == "icd-9")
# refactor neuro status
diag_table_wide_all$pns_cns <- factor(diag_table_wide_all$pns_cns,
levels=c("Central", "Peripheral"),
labels=c("CNS", "PNS"))
return(diag_table_wide_all)
}
stratified_neuro_counts_adult <- compute_stratified_neuro_counts(sorted_sites = adult_sites,
is_pediatric = FALSE,
tableone = tableone_adult)
stratified_neuro_counts_pediatric <- compute_stratified_neuro_counts(sorted_sites = pediatric_sites,
is_pediatric = TRUE,
tableone = tableone_pediatric)
compute_stratified_neuro_counts_age <- function(stratified_neuro_counts, tableone) {
stratified_neuro_counts$variable <- factor(stratified_neuro_counts$variable,
levels=c(".00to02", ".03to05", ".06to11", ".12to17",
".18to25", ".26to49", ".50to69", ".70to79", ".80plus"),
labels=c("0-2 Years", "3-5 Years", "6-11 Years", "12-17 Years",
"18-25 Years", "26-49 Years", "50-69 Years", "70-79 Years", "80+ Years"))
# total counts
n_0_2 <- get_table_n(tableone, var = "0-2")
n_3_5 = get_table_n(tableone, var = "3-5")
n_6_11 = get_table_n(tableone, var = "6-11")
n_12_17 = get_table_n(tableone, var = "12-17")
n_18_25 = get_table_n(tableone, var = "18-25")
n_26_49 = get_table_n(tableone, var = "26-49")
n_50_69 = get_table_n(tableone, var = "50-69")
n_70_79 = get_table_n(tableone, var = "70-79")
n_80 = get_table_n(tableone, var = "80+")
age_df <- data.frame("variable" = c("0-2 Years",
"3-5 Years",
"6-11 Years",
"12-17 Years",
"18-25 Years",
"26-49 Years",
"50-69 Years",
"70-79 Years",
"80+ Years"),
"n_var" = c(n_0_2,
n_3_5,
n_6_11,
n_12_17,
n_18_25,
n_26_49,
n_50_69,
n_70_79,
n_80))
stratified_neuro_counts <- stratified_neuro_counts %>%
filter(grepl("Years", variable),
!code == "NN",
!total_n_var == 0) %>%
# total_n would be calculating total codes across each age group which is not true reflection of patient counts b/c patients can have more than one code
#mutate(total_n = sum(total_n_var)) %>%
ungroup() %>%
left_join(., age_df, by = "variable") %>%
mutate(n_var = as.numeric(n_var),
perc = as.character(round(total_n_var/n_var*100,1)),
perc = if_else(perc < 0.1, '<0.1', perc),
perc = paste0(perc, "%"))
return(stratified_neuro_counts)
}
stratified_neuro_counts_age_adult <- compute_stratified_neuro_counts_age(stratified_neuro_counts = stratified_neuro_counts_adult,
tableone = tableone_adult)
stratified_neuro_counts_age_pediatric <- compute_stratified_neuro_counts_age(stratified_neuro_counts = stratified_neuro_counts_pediatric,
tableone = tableone_pediatric)
Diagnoses by Adult and Pediatric Populations
n_0_2 <- get_table_n(tableone_combine, var = "0-2")
#n_3_5 <- get_table_n(tableone_combine, var = "3-5")
n_6_11 <- get_table_n(tableone_combine, var = "6-11")
n_12_17 <- get_table_n(tableone_combine, var = "12-17")
total_ped_age_count = as.numeric(n_0_2) + as.numeric(n_6_11) + as.numeric(n_12_17)
stratified_neuro_counts_age_pediatric_reformat = stratified_neuro_counts_age_pediatric %>%
select(variable, pns_cns, description, n_var, total_n_var) %>%
mutate(variable = "0-17 Years") %>%
group_by(pns_cns, description) %>%
mutate(total_n_var = sum(total_n_var, na.rm = TRUE)) %>%
ungroup() %>%
# n_var is the total per `age_group` stratification
select(-n_var) %>%
distinct() %>%
mutate(perc = round(total_n_var/total_ped_age_count*100,1),
perc = if_else(perc < 0.1, '<0.1', as.character(perc)),
perc = paste0(perc, "%"))
stratified_neuro_counts_age_combined <- stratified_neuro_counts_age_adult %>%
select(variable, pns_cns, description, total_n_var, perc) %>%
rbind(., stratified_neuro_counts_age_pediatric_reformat %>%
select(variable, pns_cns, description, total_n_var, perc))
stratified_neuro_counts_age_combined$variable <- factor(stratified_neuro_counts_age_combined$variable,
levels=c("0-17 Years", "18-25 Years", "26-49 Years", "50-69 Years", "70-79 Years", "80+ Years"))
group.colors <- c(NNC = "gray", PNS = "slateblue", CNS ="tomato")
adult_ped_combined_plot <- ggplot(stratified_neuro_counts_age_combined,
aes(x = total_n_var, y = reorder(description,
total_n_var),
fill = pns_cns)) +
geom_bar(stat = "identity") +
geom_text(
aes(x = total_n_var, y = description, label = perc),
hjust = -0.1,
#hjust="inward",
size = 6,
inherit.aes = TRUE) +
scale_x_continuous(expand = expansion(mult = c(0,0.2))) + # to ensure percent labels are within the figure
facet_grid(pns_cns ~ variable, scales = "free", space = "free_y") +
scale_y_discrete("", position="left", labels = function(x) str_wrap(x, width = 100)) +
scale_fill_manual(values = group.colors) +
xlab("Number of Patients") +
theme(legend.position = "none",
strip.text.y = element_text(size = 40),
strip.text.x = element_text(size = 35),
axis.text.y.left = element_text(size = 25, face = "bold"),
axis.text.x = element_text(size = 25),
axis.title.x = element_text(size = 35))
ggsave("figures/Fig3_diagnoses_by_age_adult_ped_18group.png", adult_ped_combined_plot, width = 40, height = 19, dpi = 300)

Evaluate Diagnoses by Outcome
stratified_neuro_counts_adult$population = "adult"
stratified_neuro_counts_pediatric$population = "pediatric"
stratified_neuro_counts_combine <- rbind(stratified_neuro_counts_adult, stratified_neuro_counts_pediatric)
## determine total number of patients who died or severe
n_death_adult = get_table_n(df = tableone_adult, var = "Deceased") %>% as.numeric()
n_severe_adult = get_table_n(df = tableone_adult, var = "Severe") %>% as.numeric()
n_death_pediatric = get_table_n(df = tableone_pediatric, var = "Deceased") %>% as.numeric()
n_severe_pediatric = get_table_n(df = tableone_pediatric, var = "Severe") %>% as.numeric()
stratified_neuro_counts_outcomes <- stratified_neuro_counts_combine %>%
filter(variable %in% c("Severe", "Deceased"),
!code == "NN",
!total_n_var == 0) %>%
# total_n would be calculating total codes across each outcome group which is not true reflection of patient counts b/c patients can have more than one code
#mutate(total_n = sum(total_n_var)) %>%
group_by(variable, description, population) %>%
mutate(total_n_var = sum(total_n_var)) %>%
ungroup() %>%
mutate(perc = case_when(variable == "Severe" & population == "adult" ~ round(total_n_var/n_severe_adult*100,1),
variable == "Deceased" & population == "adult" ~ round(total_n_var/n_death_adult*100,1),
variable == "Severe" & population == "pediatric" ~ round(total_n_var/n_severe_pediatric*100,1),
variable == "Deceased" & population == "pediatric" ~ round(total_n_var/n_death_pediatric*100,1)),
perc = if_else(perc < 0.1, '<0.1', as.character(perc)),
perc = paste0(perc, "%")) %>%
select(-code, -concept_type) %>%
distinct()
adult_outcomes = ggplot(stratified_neuro_counts_outcomes %>%
filter(population == "adult") %>%
mutate(variable = as.factor(variable) %>%
fct_recode(
Mortality = "Deceased")),
aes(x = total_n_var, y = reorder(description, total_n_var), fill = pns_cns)) +
geom_bar(stat = "identity") +
geom_text(
aes(x = total_n_var, y = description, label = perc),
hjust = -0.1,
#hjust="inward",
size = 5,
inherit.aes = TRUE) +
scale_x_continuous(expand = expansion(mult = c(0,0.2))) +
facet_grid(pns_cns ~ variable, scales = "free", space = "free_y") +
scale_y_discrete("", position="left", labels = function(x) str_wrap(x, width = 100)) +
scale_fill_manual(values = group.colors) +
xlab("Number of Patients") +
theme(legend.position = "none",
strip.text.y = element_text(size = 35),
strip.text.x = element_text(size = 30),
axis.text.y.left = element_text(size = 20, face = "bold"),
axis.text.x = element_text(size = 15),
axis.title.x = element_text(size = 35))
ggsave("figures/SuppFig1_diagnoses_by_outcomes_adult.png", adult_outcomes, width = 25, height = 15, dpi = 300)
pediatric_outcomes = ggplot(stratified_neuro_counts_outcomes %>%
filter(population == "pediatric") %>%
mutate(variable = as.factor(variable) %>%
fct_recode(
Mortality = "Deceased")),
aes(x = total_n_var, y = reorder(description, total_n_var), fill = pns_cns)) +
geom_bar(stat = "identity") +
geom_text(
aes(x = total_n_var, y = description, label = perc),
hjust = -0.1,
#hjust="inward",
size = 5,
inherit.aes = TRUE) +
scale_x_continuous(expand = expansion(mult = c(0,0.2))) +
facet_grid(pns_cns ~ variable, scales = "free") +
scale_y_discrete("", position="left", labels = function(x) str_wrap(x, width = 100)) +
scale_fill_manual(values = group.colors) +
xlab("") +
theme(legend.position = "none",
strip.text.y = element_text(size = 35),
strip.text.x = element_text(size = 30),
axis.text.y.left = element_text(size = 20, face = "bold"),
axis.text.x = element_text(size = 15),
axis.title.x = element_text(size = 35))
ggsave("figures/SuppFig1_diagnoses_by_outcomes_pediatric.png", pediatric_outcomes, width = 25, height = 9, dpi = 300)
#grid.arrange(pediatric_outcomes, adult_outcomes, ncol=1)
ggsave("figures/SuppFig1_diagnoses_by_outcome_combined.png", grid.arrange(pediatric_outcomes, adult_outcomes, ncol=1), width = 25, height = 25, dpi = 300)

Evaluate patients with “Both” CNS and PNS diseases
These patients were excluded from the primary analysis due to
potential confounding.
# create empty list to store the counts of "both" patients
both_counts_list <- list()
# for each healthcare system, retrieve the number of excluded "both" patients recorded
for (i in sorted_sites) {
n_both <- results[[paste(i)]][["first_hosp_results"]][["both_counts"]]
both_counts_list[[i]] <- n_both
}
both_counts <- bind_rows(both_counts_list) %>% t() %>% data.frame() %>% sum()
print(paste(both_counts, "(", round(both_counts/(as.numeric(tableone_combine$N[1])+both_counts)*100,2), "%)"))
## [1] "1990 ( 1.84 %)"
Comorbidity Analysis
Create comorbidity count table
comorbidity_table_adult <- comorb_table_wide %>%
select(Comorbidity, population, Comorb_Total, `NNC_N_%`, `CNS_N_%`, `PNS_N_%`) %>%
filter(population == "Adult") %>%
rename(N = "Comorb_Total",
NNC = "NNC_N_%",
CNS = "CNS_N_%",
PNS = "PNS_N_%") %>%
mutate(N = paste0(N, " (", round(N/n_adult, 2)*100, "%)"))
comorbidity_table_pediatric <- comorb_table_wide %>%
select(Comorbidity, population, Comorb_Total, `NNC_N_%`, `CNS_N_%`, `PNS_N_%`) %>%
filter(population == "Pediatric") %>%
rename(N = "Comorb_Total",
NNC = "NNC_N_%",
CNS = "CNS_N_%",
PNS = "PNS_N_%") %>%
mutate(N = paste0(N, " (", round(N/n_pediatric, 2)*100, "%)"))
supp_comorbidity_table <- rbind(comorbidity_table_adult, comorbidity_table_pediatric) %>%
arrange(Comorbidity)
write.csv(supp_comorbidity_table, "tables/SuppTable_Comorbidity_Counts.csv", row.names = FALSE)
Create vector counts of patients with comorbidity data
neuro_pt_counts_sum_adult <- neuro_pt_counts %>%
filter(population == "Adult") %>%
select(-population)
neuro_pt_counts_sum_pediatric <- neuro_pt_counts %>%
filter(population == "Pediatric") %>%
select(-population)
none_n_adult = neuro_pt_counts_sum_adult$None_n
cns_n_adult = neuro_pt_counts_sum_adult$CNS_n
pns_n_adult = neuro_pt_counts_sum_adult$PNS_n
none_n_pediatric = neuro_pt_counts_sum_pediatric$None_n
cns_n_pediatric = neuro_pt_counts_sum_pediatric$CNS_n
pns_n_pediatric = neuro_pt_counts_sum_pediatric$PNS_n
Calculate Relative Risk (RR)
calculate_rr <- function(is_pediatric = FALSE) {
if(is_pediatric == FALSE) {
cns_n = cns_n_adult
pns_n = pns_n_adult
neuro_pt_counts_sum = neuro_pt_counts_sum_adult
pop = "Adult"
# create a list of comorbidities
list_of_comorbs <- comorb_table_wide %>%
filter(population == paste(pop)) %>%
distinct(Comorbidity)
list_of_comorbs <- list_of_comorbs$Comorbidity
} else {
cns_n = cns_n_pediatric
pns_n = pns_n_pediatric
neuro_pt_counts_sum = neuro_pt_counts_sum_pediatric
pop = "Pediatric"
# create a list of comorbidities
list_of_comorbs <- comorb_table_wide %>%
filter(population == paste(pop)) %>%
distinct(Comorbidity) %>%
as.vector()
list_of_comorbs <- list_of_comorbs$Comorbidity
}
# create empty list to save relative risk calculations
rr_calcs <- list()
# for each comorbidity, we will calculate the relative risk of having a CNS and PNS diagnosis
for (i in list_of_comorbs) {
# https://www.cdc.gov/csels/dsepd/ss1978/lesson3/section5.html
# create matrix as:
# exposed group - with outcome, without outcome
# non exposed group - with outcome, without outcome
# where variables ending with `_Total` are the total number of CNS, PNS, or NNC patients with the respective comorbidity.
# variables ending with `_n` are the total numbers in the entire population
# to calculate confidence intervals
#https://sphweb.bumc.bu.edu/otlt/mph-modules/bs/bs704_confidence_intervals/bs704_confidence_intervals8.html
rr_cns <- comorb_table_wide %>%
filter(population == paste(pop),
Comorbidity == paste(i)) %>%
mutate(a = CNS_Total, # number CNS w/comorb
b = Comorb_Total - CNS_Total, # number of comorb pts w/o CNS
c = cns_n - CNS_Total, # number of CNS pts w/o comorb
d = sum(neuro_pt_counts_sum) - Comorb_Total - c, # number of pts w/o comorb or CNS
risk_cns = a/(a+b),
risk_non_cns = c/(c+d),
rr_CNS = risk_cns/risk_non_cns,
# for confidence intervals
ci_1 = (b/a)/(a+b), # (n1-x1)/n1
ci_2 = (d/c)/(c+d), # (n2-x2)/n2
ci_root = sqrt(ci_1 + ci_2),
ci_lower = log(rr_CNS) - (1.96*ci_root),
ci_upper = log(rr_CNS) + (1.96*ci_root),
l_ci_cns = exp(ci_lower),
u_ci_cns = exp(ci_upper)) %>%
select(Comorbidity, Comorb_Total, None_Total, CNS_Total, PNS_Total, rr_CNS, l_ci_cns, u_ci_cns)
rr_pns <- comorb_table_wide %>%
filter(population == paste(pop),
Comorbidity == paste(i)) %>%
mutate(a = PNS_Total, # number PNS w/comorb
b = Comorb_Total - PNS_Total, # number of comorb pts w/o PNS
c = pns_n - PNS_Total, # number of PNS pts w/o comorb
d = sum(neuro_pt_counts_sum) - Comorb_Total - c, # number of pts w/o comorb or PNS
risk_pns = a/(a+b),
risk_non_pns = c/(c+d),
rr_PNS = risk_pns/risk_non_pns,
# for confidence intervals
ci_1 = (b/a)/(a+b),
ci_2 = (d/c)/(c+d),
ci_root = sqrt(ci_1 + ci_2),
ci_lower = log(rr_PNS) - (1.96*ci_root),
ci_upper = log(rr_PNS) + (1.96*ci_root),
l_ci_pns = exp(ci_lower),
u_ci_pns = exp(ci_upper)) %>%
select(Comorbidity, rr_PNS, l_ci_pns, u_ci_pns)
rr <- rr_cns %>%
left_join(., rr_pns, by = c("Comorbidity"))
rr_calcs[[i]] <- rr
}
rr_results <- bind_rows(rr_calcs) %>%
mutate(rr_CNS = round(rr_CNS, 2),
l_ci_cns = round(l_ci_cns, 2),
u_ci_cns = round(u_ci_cns, 2),
rr_PNS = round(rr_PNS, 2),
l_ci_pns = round(l_ci_pns, 2),
u_ci_pns = round(u_ci_pns, 2),
`RR CNS (95% CI)` = paste(rr_CNS, "(", l_ci_cns, ",", u_ci_cns, ")"),
`RR PNS (95% CI)` = paste(rr_PNS, "(", l_ci_pns, ",", u_ci_pns, ")")) %>%
select(Comorbidity, rr_CNS, l_ci_cns, u_ci_cns, `RR CNS (95% CI)`,
rr_PNS, l_ci_pns, u_ci_pns, `RR PNS (95% CI)`)
# tidy up data
write.csv(rr_results %>%
arrange(desc(rr_CNS)) %>%
select(Comorbidity, `RR CNS (95% CI)`, `RR PNS (95% CI)`),
paste0("tables/Supp.Table4_RR_", pop, ".csv"), row.names = FALSE)
# reformat data
rr_results_tidy_rr <- rr_results %>%
select(Comorbidity, rr_CNS, rr_PNS) %>%
pivot_longer(cols = rr_CNS:rr_PNS, names_to = "Neuro Status", values_to = "RR") %>%
mutate(`Neuro Status` = if_else(`Neuro Status` == "rr_CNS", "CNS", "PNS"))
rr_results_tidy_l_ci <- rr_results %>%
select(Comorbidity, l_ci_cns, l_ci_pns) %>%
pivot_longer(cols = l_ci_cns:l_ci_pns, names_to = "Neuro Status", values_to = "l_CI") %>%
mutate(`Neuro Status` = if_else(`Neuro Status` == "l_ci_cns", "CNS", "PNS"))
rr_results_tidy_u_ci <- rr_results %>%
select(Comorbidity, u_ci_cns, u_ci_pns) %>%
pivot_longer(cols = u_ci_cns:u_ci_pns, names_to = "Neuro Status", values_to = "u_CI") %>%
mutate(`Neuro Status` = if_else(`Neuro Status` == "u_ci_cns", "CNS", "PNS"))
# combine all tidy results
rr_results_tidy <- rr_results_tidy_rr %>%
left_join(., rr_results_tidy_l_ci,
by = c("Comorbidity", "Neuro Status")) %>%
left_join(., rr_results_tidy_u_ci,
by = c("Comorbidity", "Neuro Status"))
# order by CNS diagnoses with higher RR
cns_top_results <- rr_results_tidy %>%
filter(`Neuro Status` == "CNS") %>%
arrange(RR) %>%
select(Comorbidity)
rr_results_tidy$Comorbidity <- ordered(rr_results_tidy$Comorbidity, levels = cns_top_results$Comorbidity)
cns_rr_plot <- ggplot(rr_results_tidy %>%
filter(`Neuro Status` == "CNS"),
aes(x = RR, y = Comorbidity)) +
geom_vline(aes(xintercept = 1), size = .25, linetype = "dashed") +
geom_errorbarh(aes(xmax = u_CI, xmin = l_CI), size = 0.7, height = 0.2, color = "gray50") +
geom_point(size = 1.5, color = "tomato") +
scale_x_continuous(breaks = seq(0, 4, 1), labels = seq(0, 4, 1),
limits = c(0,4)) +
ylab("") +
xlab("Relative Risk") +
ggtitle("CNS Patients") +
theme(axis.text.y = element_text(face = "bold"))
# arrange by top PNS results
pns_top_results <- rr_results_tidy %>%
filter(`Neuro Status` == "PNS") %>%
arrange(RR) %>%
select(Comorbidity)
rr_results_tidy$Comorbidity <- ordered(rr_results_tidy$Comorbidity, levels = pns_top_results$Comorbidity)
pns_rr_plot <- ggplot(rr_results_tidy %>%
filter(`Neuro Status` == "PNS"),
aes(x = RR, y = Comorbidity)) +
geom_vline(aes(xintercept = 1), size = .25, linetype = "dashed") +
geom_errorbarh(aes(xmax = u_CI, xmin = l_CI), size = 0.70, height = 0.2, color = "gray50") +
geom_point(size = 1.5, color = "slateblue") +
scale_x_continuous(breaks = seq(0, 1.5, 0.5), labels = seq(0, 1.5, 0.5),
limits = c(0,1.5)) +
ylab("") +
xlab("Relative Risk") +
ggtitle("PNS Patients") +
theme(axis.text.y = element_text(face = "bold"))
pns_rr_plot <- set_panel_size(pns_rr_plot,
width = unit(7, "cm"),
height = unit(6.5, "in"))
cns_rr_plot <- set_panel_size(cns_rr_plot,
width = unit(7, "cm"),
height = unit(6.5, "in"))
#grid.arrange(cns_rr_plot, pns_rr_plot, ncol=2)
ggsave(paste0("figures/Figure4.relative_risk_", pop, ".png"), grid.arrange(cns_rr_plot, pns_rr_plot, ncol=2), width = 15, height = 8, dpi = 300)
}

LPCA deviance explained
How much deviance is explained with 10 principal components for 30
comorbidity types?
Across all healthcare systems, 10 principal components explained
above 75% of the deviance.
compare_deviance <- function(sorted_sites, is_pediatric=FALSE) {
pca_dev <- list()
if(is_pediatric==FALSE) {
population = "adults"
sorted_sites = sorted_sites[!sorted_sites%in% c("GOSH_results", "BCH_results")]
} else {
population = "pediatrics"
}
# for each healthcare system, we will calculate the total number of patients with each ICD code
for (i in sorted_sites) {
print(i)
tmp <- get(i)
tmp_pca <- tmp[[c(
"comorbidities",
paste0("comorb_", population),
"deviance_expl"
)]] %>%
data.frame() %>%
rename(dev_expl = '.')
tmp_pca$site <- tmp[["site"]]
pca_dev[[i]] <- tmp_pca
}
pca_df <- bind_rows(pca_dev)
return(pca_df)
}
pca_adult <- compare_deviance(sorted_sites = adult_sites, is_pediatric = FALSE)
## [1] "APHP_results"
## [1] "FRBDX_results"
## [1] "UKFR_results"
## [1] "ICSM_results"
## [1] "HPG23_results"
## [1] "NUH_results"
## [1] "MGB_results"
## [1] "UPENN_results"
## [1] "UMICH_results"
## [1] "NWU_results"
## [1] "UPITT_results"
## [1] "H12O_results"
## [1] "UKY_results"
## [1] "VA1_results"
## [1] "VA2_results"
## [1] "VA3_results"
## [1] "VA4_results"
## [1] "VA5_results"
## [1] "UCLA_results"
pca_data <- bind_rows(pca_adult)
theme_set(theme_classic())
pca_plot <- ggplot(pca_data,
aes(x = fct_reorder(site, -dev_expl), y = dev_expl, group = 1)) +
geom_point(size = 2.5) +
geom_line() +
xlab("Healthcare System") +
ylab("Proportion of Deviance explained") +
ylim(0,1) +
theme(axis.text.x=element_text(angle=90, hjust=1, face = "bold", size = 12),
axis.text.y=element_text(face = "bold"),
axis.title.y = element_text(face = "bold", size = 15)); pca_plot

ggsave("figures/SuppFig_pca_deviance.png", pca_plot, height = 6, width = 6)
LS0tDQp0aXRsZTogIioqQ09WSUQtMTkgYW5kIE5ldXJvbG9naWNhbCBJbGxuZXNzIC0gUGF0aWVudCBDaGFyYWN0ZXJpc3RpY3MqKiINCmF1dGhvcjogIk1lZyBIdXRjaCwgSmkgU29uLCBUcmFuZyBMZSwgQ2h1YW4gSG9uZywgWHVhbiBXYW5nLCBaYWhyYSBTaGFrZXJpIg0KZGF0ZTogIjA0LzExLzIwMjMiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHRoZW1lOiBzcGFjZWxhYg0KLS0tDQoNClRoaXMgbm90ZWJvb2sgZGVzY3JpYmVzIHRoZSAqKjEwNiwyMjkqKiBob3NwaXRhbGl6ZWQgQ09WSUQtMTkgcG9zaXRpdmUgcGF0aWVudHMgdGhhdCB3ZXJlIHN0dWRpZWQgYXMgcGFydCBvZiB0aGUgW0NvbnNvcnRpdW0gZm9yIENsaW5pY2FsIENoYXJhY3Rlcml6YXRpb24gb2YgQ09WSUQtMTkgYnkgRUhSXShodHRwczovL2NvdmlkY2xpbmljYWwubmV0LykgTmV1cm9sb2d5IGdyb3VwLg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGdyaWRHcmFwaGljcykNCmxpYnJhcnkoZ3JpZEV4dHJhKQ0KbGlicmFyeShnZ3B1YnIpDQpsaWJyYXJ5KERUKQ0KbGlicmFyeShrYWJsZUV4dHJhKQ0KbGlicmFyeShjb3dwbG90KQ0KbGlicmFyeShSQ29sb3JCcmV3ZXIpDQpsaWJyYXJ5KGdsdWUpDQpsaWJyYXJ5KGVnZykNCnNvdXJjZSgiUi9wbG90X3RoZW1lLlIiKQ0Kc291cmNlKCJSL2RlbW9fdGFibGVzLlIiKQ0Kc291cmNlKCJSL3V0aWxzLlIiKQ0KYGBgDQoNCiMgKipJbXBvcnQgRGF0YSBmcm9tIGVhY2ggSGVhbHRoY2FyZSBzeXN0ZW0qKg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyByZWFkIGluIGZpbGVzIGZyb20gcmVzdWx0cyBmb2xkZXIgDQojIHRoaXMgZm9sZGVyIGNvbnRhaW5zIGFsbCBvZiB0aGUgbG9jYWwgaGVhbHRoY2FyZSBzeXN0ZW0gbGV2ZWwgYW5hbHlzZXMNCnJkYXMgPC0gbGlzdC5maWxlcygNCiAgcGF0aCA9ICJyZXN1bHRzIiwNCiAgcGF0dGVybiA9ICIucmRhIiwNCiAgZnVsbC5uYW1lcyA9IFRSVUUNCikNCg0KDQpmb3IgKHJkYSBpbiByZGFzKSB7DQogIGxvYWQocmRhKQ0KfQ0KDQpybShyZGFzLCByZGEpDQoNCiMgY3JlYXRlIGEgbGlzdCBvZiBwYXJ0aWNpcGF0aW5nIGhlYWx0aGNhcmUgc3lzdGVtcyBmcm9tIG91ciBzdHVkeSB0cmFja2luZyBzcHJlYWRzaGVldA0Kc2l0ZV9nb29nbGVfdXJsIDwtICJodHRwczovL2RvY3MuZ29vZ2xlLmNvbS9zcHJlYWRzaGVldHMvZC8xZXBjWU5kXzBqQ1VNa3RPSGY4bXo1djY1MXp5MUpBTEQ2UGd6b2JyR1dEWS9lZGl0P3VzcD1zaGFyaW5nIg0KDQojIGxvYWQgc2l0ZSBwYXJhbWV0ZXJzDQpzaXRlX3BhcmFtcyA8LSBnb29nbGVzaGVldHM0OjpyZWFkX3NoZWV0KHNpdGVfZ29vZ2xlX3VybCwgc2hlZXQgPSAxKQ0Kc2l0ZV9hdmFpbHMgPC0gZ29vZ2xlc2hlZXRzNDo6cmVhZF9zaGVldChzaXRlX2dvb2dsZV91cmwsIHNoZWV0ID0gMikNCg0KIyBmaWx0ZXIgdGhlIGxpc3Qgb2Ygc2l0ZXMgd2hvIHJhbiB0aGUgYW5hbHlzaXMNCnNvcnRlZF9zaXRlcyA8LSBzaXRlX2F2YWlscyAlPiUNCiAgZmlsdGVyKCFpcy5uYShkYXRlX3Y0X3JlY2VpdmVkKSkgJT4lDQogIHB1bGwoc2l0ZWlkKSAlPiUNCiAgcGFzdGUoInJlc3VsdHMiLCBzZXAgPSAiXyIpDQoNCiMgbGlzdCBzaXRlcyB3aXRob3V0IHJhY2UNCnNpdGVzX3dvX3JhY2UgPC0gc2l0ZV9wYXJhbXMgJT4lDQogIGZpbHRlcighaW5jbHVkZV9yYWNlKSAlPiUNCiAgcHVsbChzaXRlaWQpDQoNCiMgY29tYmluZSBhbGwgcmRhIGZpbGVzIHdpdGggJ3Jlc3VsdHMnIGluIG5hbWUNCnJlc3VsdHMgPC0gbWdldChscyhwYXR0ZXJuID0gInJlc3VsdHMiKSkNCg0KIyMgbG9hZCBpbiB0aGUgcHJlLXByb2Nlc3NlZCBjb21vcmJpZGl0eSBhbmQgbWV0YS1kYXRhDQpsb2FkKCJwcm9jZXNzZWQvY29tb3JiaWRpdHlfdGFibGVfY25wcy5yZGEiKQ0KbG9hZCgicHJvY2Vzc2VkL25ldXJvX3B0X2NvdW50cy5yZGEiKSANCmBgYA0KDQojICoqUHJlLXByb2Nlc3NpbmcqKg0KDQoqKjEuIEZvcm1hdCBwcmltYXJ5IGRlbW9ncmFwaGljIGNoYXJhY3RlcmlzdGljcyoqDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIGNyZWF0ZSBsaXN0IG9mIGhvc3BpdGFscyBmb3IgYWR1bHQgYW5kIHBlZGlhdHJpYyBhbmFseXNlcw0KYWR1bHRfc2l0ZXMgPSBzb3J0ZWRfc2l0ZXNbIXNvcnRlZF9zaXRlcyAlaW4lIGMoIkJDSF9yZXN1bHRzIiwgIkdPU0hfcmVzdWx0cyIpXQ0KDQpwZWRpYXRyaWNfc2l0ZXMgPSBzb3J0ZWRfc2l0ZXNbIXNvcnRlZF9zaXRlcyAlaW4lIGMoIlZBMV9yZXN1bHRzIiwgIlZBMl9yZXN1bHRzIiwgIlZBM19yZXN1bHRzIiwgIlZBNF9yZXN1bHRzIiwgIlZBNV9yZXN1bHRzIildDQoNCmRlbW9fdGFibGVfYWR1bHQgPC0gY3JlYXRlX2RlbW9fdGFibGVvbmUoc29ydGVkX3NpdGVzID0gYWR1bHRfc2l0ZXMsIGlzX3BlZGlhdHJpYyA9IEZBTFNFKQ0KDQpkZW1vX3RhYmxlX3BlZGlhdHJpYyA8LSBjcmVhdGVfZGVtb190YWJsZW9uZShzb3J0ZWRfc2l0ZXMgPSBwZWRpYXRyaWNfc2l0ZXMsIGlzX3BlZGlhdHJpYyA9IFRSVUUpDQoNCmRlbW9fdGFibGVfY29tYmluZSA8LSByYmluZChkZW1vX3RhYmxlX2FkdWx0LCBkZW1vX3RhYmxlX3BlZGlhdHJpYykNCmBgYA0KDQoqKjIuIEZvcm1hdCBjbGluaWNhbCBjaGFyYWN0ZXJpc3RpY3MgKGkuZTogY29udGludW91cyB2YXJpYWJsZXMgc3VjaCBhcyBtZWRpYW4gdGltZSB0byBkaXNjaGFyZ2UpKioNCg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KY2xpbmljYWxfdGFibGVfYWR1bHQgPC0gY3JlYXRlX2NsaW5pY2FsX3RhYmxlb25lKHNvcnRlZF9zaXRlcyA9IGFkdWx0X3NpdGVzLCBpc19wZWRpYXRyaWMgPSBGQUxTRSkNCg0KY2xpbmljYWxfdGFibGVfcGVkaWF0cmljIDwtIGNyZWF0ZV9jbGluaWNhbF90YWJsZW9uZShzb3J0ZWRfc2l0ZXMgPSBwZWRpYXRyaWNfc2l0ZXMsIGlzX3BlZGlhdHJpYyA9IFRSVUUpDQoNCmNsaW5pY2FsX3RhYmxlX2NvbWJpbmUgPC0gcmJpbmQoY2xpbmljYWxfdGFibGVfYWR1bHQsIGNsaW5pY2FsX3RhYmxlX3BlZGlhdHJpYykNCmBgYA0KDQoNCioqMy4gQ29uZHVjdCBhZGRpdGlvbmFsIHByZS1wcm9jZXNzaW5nIG9mIGNsaW5pY2FsIHZhcmlhYmxlcyoqDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpjbGluaWNhbF90YWJsZV9hZHVsdF9jbGVhbiA8LSBjbGVhbl9jbGluaWNhbF90YWJsZXMoY2xpbmljYWxfdGFibGUgPSBjbGluaWNhbF90YWJsZV9hZHVsdCkNCg0KY2xpbmljYWxfdGFibGVfcGVkaWF0cmljX2NsZWFuIDwtIGNsZWFuX2NsaW5pY2FsX3RhYmxlcyhjbGluaWNhbF90YWJsZSA9IGNsaW5pY2FsX3RhYmxlX3BlZGlhdHJpYykNCg0KY2xpbmljYWxfdGFibGVfY2xlYW5fY29tYmluZSA8LSByYmluZChjbGluaWNhbF90YWJsZV9hZHVsdF9jbGVhbiwgY2xpbmljYWxfdGFibGVfcGVkaWF0cmljX2NsZWFuKQ0KYGBgDQoNCioqQWRkIENvbW9yYmlkaXR5IGRhdGEqKg0KDQpgYGB7cn0NCmNvbW9yYmlkaXR5X3RhYmxlX2FkdWx0cyA8LSBjb21vcmJfdGFibGVfd2lkZSAlPiUNCiAgZmlsdGVyKHBvcHVsYXRpb24gPT0gIkFkdWx0IikgJT4lIA0KICBhcnJhbmdlKGRlc2MoQ29tb3JiX1RvdGFsKSkgJT4lIA0KICBzbGljZSgxOjQpICMgcmV0dXJuIHRvcCBjb21vcmJpZGl0aWVzDQoNCmNvbW9yYmlkaXR5X3RhYmxlX3BlZGlhdHJpYyA8LSBjb21vcmJfdGFibGVfd2lkZSAlPiUNCiAgZmlsdGVyKHBvcHVsYXRpb24gPT0gIlBlZGlhdHJpYyIpICU+JSANCiAgYXJyYW5nZShkZXNjKENvbW9yYl9Ub3RhbCkpICU+JSANCiAgc2xpY2UoMTo0KSAjIHJldHVybiB0b3AgY29tb3JiaWRpdGllcw0KDQp0b3BfY29tb3JiX2FkdWx0cyA8LSBjb21vcmJpZGl0eV90YWJsZV9hZHVsdHMkQ29tb3JiaWRpdHkNCnRvcF9jb21vcmJfcGVkaWF0cmljcyA8LSBjb21vcmJpZGl0eV90YWJsZV9wZWRpYXRyaWMkQ29tb3JiaWRpdHkNCmBgYA0KDQojICoqRGVtb2dyYXBoaWNzKioNCg0KIyMgVGFibGUgT25lIC0gQ29tYmluZWQNCg0KYGBge3J9DQpuZXVyb19wdF9jb3VudHNfc3VtIDwtIGNvbFN1bXMobmV1cm9fcHRfY291bnRzWywtMV0pICU+JSANCiAgZGF0YS5mcmFtZSgpICU+JSANCiAgdCgpICU+JSANCiAgZGF0YS5mcmFtZSgpDQoNCiMgc3BlY2lmeSB0aGUgb3JkZXIgb2YgVGFibGUgMQ0Kcm93X29yZGVyX2FkdWx0IDwtIGMoDQogICJBbGwgUGF0aWVudHMiLCAiRmVtYWxlIiwgIk1hbGUiLCAiVW5rbm93biBTZXgiLA0KICAiMC0yIiwgIjMtNSIsICI2LTExIiwgIjEyLTE3IiwgIjE4LTI1IiwNCiAgIjI2LTQ5IiwgIjUwLTY5IiwgIjcwLTc5IiwgIjgwKyIsICJVbmtub3duIEFnZSIsDQogICJBbWVyaWNhbiBJbmRpYW4iLCAiQXNpYW4iLCAiQmxhY2siLA0KICAiSGF3YWlpYW4vUGFjaWZpYyBJc2xhbmRlciIsDQogICJIaXNwYW5pYy9MYXRpbm8iLCAiV2hpdGUiLCAiT3RoZXIiLA0KICB0b3BfY29tb3JiX2FkdWx0cywgDQogICJNZWRpYW4gRWxpeGhhdXNlciBzY29yZSAgKHNkKSAiLA0KICAiTWVkaWFuIHByZSBhZG1pc3Npb24gY25zICAoc2QpICIsDQogICJNZWRpYW4gcHJlIGFkbWlzc2lvbiBwbnMgIChzZCkgIiwNCiAgIk5vbi1TZXZlcmUiLCAiU2V2ZXJlIiwNCiAgIk1lZGlhbiB0aW1lIHRvIHNldmVyZSAgKHNkKSAiLA0KICAiQWxpdmUiLCAiRGVjZWFzZWQiLA0KICAiTWVkaWFuIHRpbWUgdG8gZGVhdGggIChzZCkgIiwNCiAgIkRpc2NoYXJnZWQiLCAiTm90IERpc2NoYXJnZWQiLA0KICAiTWVkaWFuIHRpbWUgdG8gZmlyc3QgZGlzY2hhcmdlICAoc2QpICIsDQogICJOb3QgUmVhZG1pdHRlZCIsICJSZWFkbWl0dGVkIiwNCiAgIk1lZGlhbiB0aW1lIHRvIGZpcnN0IHJlYWRtaXNzaW9uICAoc2QpICIsDQogICJNZWRpYW4gbnVtYmVyIG9mIHJlYWRtaXNzaW9ucyAgKHNkKSAiDQogICkNCg0KDQp0YWJsZW9uZV9jb21iaW5lIDwtIGNyZWF0ZV90YWJsZW9uZShkZW1vX3RhYmxlID0gZGVtb190YWJsZV9jb21iaW5lLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xpbmljYWxfdGFibGUgPSBjbGluaWNhbF90YWJsZV9jbGVhbl9jb21iaW5lKSAlPiUNCiAgcmJpbmQoLiwgY29tb3JiX3RhYmxlX3dpZGUgJT4lDQogICAgICAgICAgZ3JvdXBfYnkoQ29tb3JiaWRpdHkpICU+JQ0KICAgICAgICAgIG11dGF0ZShDb21vcmJfVG90YWwgPSBzdW0oQ29tb3JiX1RvdGFsLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgIE5vbmVfc3VtID0gc3VtKE5vbmVfVG90YWwsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgQ05TX3N1bSA9IHN1bShDTlNfVG90YWwsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgUE5TX3N1bSA9IHN1bShQTlNfVG90YWwsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgTm9uZV9wZXJjID0gcm91bmQoTm9uZV9zdW0vbmV1cm9fcHRfY291bnRzX3N1bSROb25lX24qMTAwLDEpLA0KICAgICAgICAgIENlbnRyYWxfcGVyYyA9IHJvdW5kKENOU19zdW0vbmV1cm9fcHRfY291bnRzX3N1bSRDTlNfbioxMDAsMSksDQogICAgICAgICAgUGVyaXBoZXJhbF9wZXJjID0gcm91bmQoUE5TX3N1bS9uZXVyb19wdF9jb3VudHNfc3VtJFBOU19uKjEwMCwxKSwNCiAgICAgICAgICBOb25lID0gcGFzdGUoTm9uZV9zdW0sICIoIiwgTm9uZV9wZXJjLCAiKSIpLA0KICAgICAgICAgIENlbnRyYWwgPSBwYXN0ZShDTlNfc3VtLCAiKCIsIENlbnRyYWxfcGVyYywgIikiKSwNCiAgICAgICAgICBQZXJpcGhlcmFsID0gcGFzdGUoUE5TX3N1bSwgIigiLCBQZXJpcGhlcmFsX3BlcmMsICIpIikpICU+JQ0KICAgICAgICAgIGRpc3RpbmN0KENvbW9yYmlkaXR5LCBDb21vcmJfVG90YWwsIE5vbmUsIENlbnRyYWwsIFBlcmlwaGVyYWwpICU+JQ0KICAgICAgICAgIHJlbmFtZShgVGFibGUgMWAgPSAiQ29tb3JiaWRpdHkiLA0KICAgICAgICAgIE4gPSAiQ29tb3JiX1RvdGFsIikpICU+JQ0KICBzbGljZShtYXRjaChyb3dfb3JkZXJfYWR1bHQsIGBUYWJsZSAxYCkpDQoNCmtibCh0YWJsZW9uZV9jb21iaW5lICU+JQ0KICAgICAgcmVuYW1lKCJObyBOZXVyb2xvZ2ljYWwgQ29uZGl0aW9uIChOTkMpIiA9IE5vbmUpKSAlPiUNCiAgICAgIGFkZF9oZWFkZXJfYWJvdmUoYygNCiAgICAgICIgIiwNCiAgICAgICJUb3RhbCBQYXRpZW50cyIgPSAxLA0KICAgICAgIk5ldXJvbG9naWNhbCBEaXNlYXNlIiA9IDMNCiAgICAgICkpICU+JQ0KICAgICAga2FibGVfcGFwZXIoInN0cmlwZWQiLCBmdWxsX3dpZHRoID0gRikgJT4lDQogICAgICBwYWNrX3Jvd3MoIlNleCIsIDIsIDQpICU+JQ0KICAgICAgcGFja19yb3dzKCJBZ2UiLCA1LCAxMykgJT4lDQogICAgICBwYWNrX3Jvd3MoIlJhY2UgJiBFdGhuaWNpdHkiLCAxNCwgMjApICU+JQ0KICAgICAgcGFja19yb3dzKCJQYXN0IE1lZGljYWwgSGlzdG9yeSIsIDIxLCAyNykgJT4lDQogICAgICBwYWNrX3Jvd3MoIlNldmVyaXR5IiwgMjgsIDMwKSAlPiUNCiAgICAgIHBhY2tfcm93cygiU3Vydml2YWwiLCAzMSwgMzMpICU+JQ0KICAgICAgcGFja19yb3dzKCJEaXNjaGFyZ2UiLCAzNCwgMzYpICU+JQ0KICAgICAgcGFja19yb3dzKCJSZWFkbWlzc2lvbiIsIDM3LCA0MCkNCndyaXRlLmNzdih0YWJsZW9uZV9jb21iaW5lLCAndGFibGVzL3RhYmxlMS5jc3YnLCByb3cubmFtZXMgPSBGQUxTRSkNCmBgYA0KDQojIyBUYWJsZSBPbmUgLSBBZHVsdHMNCg0KYGBge3J9DQp0YWJsZW9uZV9hZHVsdCA8LSBjcmVhdGVfdGFibGVvbmUoZGVtb190YWJsZSA9IGRlbW9fdGFibGVfYWR1bHQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xpbmljYWxfdGFibGUgPSBjbGluaWNhbF90YWJsZV9hZHVsdF9jbGVhbikgJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmJpbmQoLiwgY29tb3JiaWRpdHlfdGFibGVfYWR1bHRzICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KENvbW9yYmlkaXR5LCBDb21vcmJfVG90YWwsIGBOTkNfTl8lYCwgYENOU19OXyVgLCBgUE5TX05fJWApICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVuYW1lKGBUYWJsZSAxYCA9ICJDb21vcmJpZGl0eSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTiA9ICJDb21vcmJfVG90YWwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5vbmUgPSAiTk5DX05fJSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ2VudHJhbCA9ICJDTlNfTl8lIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQZXJpcGhlcmFsID0gIlBOU19OXyUiKSkgJT4lIA0KICBzbGljZShtYXRjaChyb3dfb3JkZXJfYWR1bHQsIGBUYWJsZSAxYCkpDQoNCmtibCh0YWJsZW9uZV9hZHVsdCAlPiUNCiAgICByZW5hbWUoIk5vIE5ldXJvbG9naWNhbCBDb25kaXRpb24gKE5OQykiID0gTm9uZSkpICU+JQ0KICBhZGRfaGVhZGVyX2Fib3ZlKGMoDQogICIgIiwNCiAgIlRvdGFsIFBhdGllbnRzIiA9IDEsDQogICJOZXVyb2xvZ2ljYWwgRGlzZWFzZSIgPSAzDQogICkpICU+JQ0KICBrYWJsZV9wYXBlcigic3RyaXBlZCIsIGZ1bGxfd2lkdGggPSBGKSAlPiUNCiAgcGFja19yb3dzKCJTZXgiLCAyLCA0KSAlPiUNCiAgcGFja19yb3dzKCJBZ2UiLCA1LCA5KSAlPiUNCiAgcGFja19yb3dzKCJSYWNlICYgRXRobmljaXR5IiwgMTAsIDE2KSAlPiUNCiAgcGFja19yb3dzKCJQYXN0IE1lZGljYWwgSGlzdG9yeSIsIDE3LCAyMykgJT4lDQogIHBhY2tfcm93cygiU2V2ZXJpdHkiLCAyNCwgMjYpICU+JQ0KICBwYWNrX3Jvd3MoIlN1cnZpdmFsIiwgMjcsIDI5KSAlPiUNCiAgcGFja19yb3dzKCJEaXNjaGFyZ2UiLCAzMCwgMzIpICU+JQ0KICBwYWNrX3Jvd3MoIlJlYWRtaXNzaW9uIiwgMzMsIDM2KQ0KYGBgDQoNCiMjIFRhYmxlIE9uZSAtIFBlZGlhdHJpY3MNCg0KYGBge3J9DQojIHNwZWNpZnkgdGhlIG9yZGVyIG9mIFRhYmxlIDENCnJvd19vcmRlcl9wZWRpYXRyaWMgPC0gYygNCiAgIkFsbCBQYXRpZW50cyIsICJGZW1hbGUiLCAiTWFsZSIsICJVbmtub3duIFNleCIsDQogICIwLTIiLCAiMy01IiwgIjYtMTEiLCAiMTItMTciLCAiMTgtMjUiLA0KICAiMjYtNDkiLCAiNTAtNjkiLCAiNzAtNzkiLCAiODArIiwgIlVua25vd24gQWdlIiwNCiAgIkFtZXJpY2FuIEluZGlhbiIsICJBc2lhbiIsICJCbGFjayIsDQogICJIYXdhaWlhbi9QYWNpZmljIElzbGFuZGVyIiwNCiAgIkhpc3BhbmljL0xhdGlubyIsICJXaGl0ZSIsICJPdGhlciIsDQogIHRvcF9jb21vcmJfcGVkaWF0cmljcywgDQogICJNZWRpYW4gRWxpeGhhdXNlciBzY29yZSAgKHNkKSAiLA0KICAiTWVkaWFuIHByZSBhZG1pc3Npb24gY25zICAoc2QpICIsDQogICJNZWRpYW4gcHJlIGFkbWlzc2lvbiBwbnMgIChzZCkgIiwNCiAgIk5vbi1TZXZlcmUiLCAiU2V2ZXJlIiwNCiAgIk1lZGlhbiB0aW1lIHRvIHNldmVyZSAgKHNkKSAiLA0KICAiQWxpdmUiLCAiRGVjZWFzZWQiLA0KICAiTWVkaWFuIHRpbWUgdG8gZGVhdGggIChzZCkgIiwNCiAgIkRpc2NoYXJnZWQiLCAiTm90IERpc2NoYXJnZWQiLA0KICAiTWVkaWFuIHRpbWUgdG8gZmlyc3QgZGlzY2hhcmdlICAoc2QpICIsDQogICJOb3QgUmVhZG1pdHRlZCIsICJSZWFkbWl0dGVkIiwNCiAgIk1lZGlhbiB0aW1lIHRvIGZpcnN0IHJlYWRtaXNzaW9uICAoc2QpICIsDQogICJNZWRpYW4gbnVtYmVyIG9mIHJlYWRtaXNzaW9ucyAgKHNkKSAiDQogICkNCg0KdGFibGVvbmVfcGVkaWF0cmljIDwtIGNyZWF0ZV90YWJsZW9uZShkZW1vX3RhYmxlID0gZGVtb190YWJsZV9wZWRpYXRyaWMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsaW5pY2FsX3RhYmxlID0gY2xpbmljYWxfdGFibGVfcGVkaWF0cmljX2NsZWFuKSAgJT4lDQogIHJiaW5kKC4sIGNvbW9yYmlkaXR5X3RhYmxlX3BlZGlhdHJpYyAlPiUNCiAgICAgICAgICBzZWxlY3QoQ29tb3JiaWRpdHksIENvbW9yYl9Ub3RhbCwgYE5OQ19OXyVgLCBgQ05TX05fJWAsIGBQTlNfTl8lYCkgJT4lIA0KICAgICAgICAgIHJlbmFtZShgVGFibGUgMWAgPSAiQ29tb3JiaWRpdHkiLA0KICAgICAgICAgICAgICAgICAgTiA9ICJDb21vcmJfVG90YWwiLA0KICAgICAgICAgICAgICAgICAgTm9uZSA9ICJOTkNfTl8lIiwNCiAgICAgICAgICAgICAgICAgIENlbnRyYWwgPSAiQ05TX05fJSIsDQogICAgICAgICAgICAgICAgICBQZXJpcGhlcmFsID0gIlBOU19OXyUiKSkgJT4lIA0KICBzbGljZShtYXRjaChyb3dfb3JkZXJfcGVkaWF0cmljLCBgVGFibGUgMWApKQ0KDQprYmwodGFibGVvbmVfcGVkaWF0cmljICU+JQ0KICAgICAgcmVuYW1lKCJObyBOZXVyb2xvZ2ljYWwgQ29uZGl0aW9uIChOTkMpIiA9IE5vbmUpKSAlPiUNCiAgYWRkX2hlYWRlcl9hYm92ZShjKA0KICAiICIsDQogICJUb3RhbCBQYXRpZW50cyIgPSAxLA0KICAiTmV1cm9sb2dpY2FsIERpc2Vhc2UiID0gMw0KICApKSAlPiUNCiAga2FibGVfcGFwZXIoInN0cmlwZWQiLCBmdWxsX3dpZHRoID0gRikgJT4lDQogIHBhY2tfcm93cygiU2V4IiwgMiwgNCkgJT4lDQogIHBhY2tfcm93cygiQWdlIiwgNSwgOCkgJT4lDQogIHBhY2tfcm93cygiUmFjZSAmIEV0aG5pY2l0eSIsIDksIDE1KSAlPiUNCiAgcGFja19yb3dzKCJQYXN0IE1lZGljYWwgSGlzdG9yeSIsIDE2LCAyMikgJT4lDQogIHBhY2tfcm93cygiU2V2ZXJpdHkiLCAyMywgMjUpICU+JQ0KICBwYWNrX3Jvd3MoIlN1cnZpdmFsIiwgMjYsIDI4KSAlPiUNCiAgcGFja19yb3dzKCJEaXNjaGFyZ2UiLCAyOSwgMzEpICU+JQ0KICBwYWNrX3Jvd3MoIlJlYWRtaXNzaW9uIiwgMzIsIDM1KQ0KYGBgDQoNCioqU2FtcGxlIFNpemUqKg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0Kbl9hZHVsdCA8LSBhcy5udW1lcmljKHRhYmxlb25lX2FkdWx0JE4pWzFdDQpuX3BlZGlhdHJpYyA8LSBhcy5udW1lcmljKHRhYmxlb25lX3BlZGlhdHJpYyROKVsxXQ0KYGBgDQoNCiMgKipEZW1vZ3JhcGhpYyAmIENsaW5pY2FsIENvdXJzZSBBbmFseXNpcyoqDQoNCkV2YWx1YXRlIGNoYXJhY3RlcmlzdGljcyBhbW9uZyBuZXVybyBzdGF0dXMgZ3JvdXBzDQoNCioqMS4gQ29uZHVjdCBjaGktc3F1YXJlZCB0ZXN0cyBmb3IgY2F0ZWdvcmljYWwgZGVtb2dyYXBoaWMgdmFyaWFibGVzKioNCg0KYGBge3J9DQpjb21wdXRlX2NoaV9zcXVhcmUgPC0gZnVuY3Rpb24odGFibGVvbmUsIGRlbW9fdGFibGUpIHsNCiAgDQogIE4gPSB0YWJsZW9uZSAlPiUgc2VsZWN0KE4pICU+JSBoZWFkKDEpICU+JSBhcy5udW1lcmljKCkNCg0KICAjIHN1bSBvZiBhbGwgbmV1cm8gY29uZGl0aW9uIGdyb3Vwcw0KICBjbnNfbiA9IGFzLm51bWVyaWMoZ3N1YiggIiAuKiQiLCAiIiwgdGFibGVvbmVbMSwiQ2VudHJhbCJdKSkNCiAgcG5zX24gPSBhcy5udW1lcmljKGdzdWIoICIgLiokIiwgIiIsIHRhYmxlb25lWzEsIlBlcmlwaGVyYWwiXSkpDQogIG5vbmVfbiA9IGFzLm51bWVyaWMoZ3N1YiggIiAuKiQiLCAiIiwgdGFibGVvbmVbMSwiTm9uZSJdKSkNCiAgDQogICMgc3VtIHVwIHRoZSB0b3RhbCBjb3VudHMgb2YgZWFjaCBkZW1vZ3JhcGhpYyB2YXJpYWJsZSBhY3Jvc3MgaGVhbHRoY2FyZSBzeXN0ZW1zDQogIHRhYmxlT25lX3N1bXMgPC0gZGVtb190YWJsZSAlPiUNCiAgICBncm91cF9ieSh2YXJpYWJsZSwgRGVtb192YXIsIERlbW9fdmFyX2kpICU+JQ0KICAgIHN1bW1hcmlzZShhY3Jvc3MoDQogICAgICBzdGFydHNfd2l0aCgibl92YXIiKSwNCiAgICAgIGZ1bmN0aW9uKHgpIHN1bSh4LCBuYS5ybSA9IFRSVUUpDQogICAgKSwgLmdyb3VwcyA9ICJkcm9wIikNCiAgDQogICMgY3JlYXRlIGxpc3Qgb2YgZGVtb2dyYXBoaWMvY2xpbmljYWwgdmFyaWFibGVzDQogIHZhcnMgPSB0YWJsZU9uZV9zdW1zJHZhcmlhYmxlDQogIA0KICAjIGZvciBiaW5hcnkgdmFyaWFibGVzLCB3ZSBzaG91bGQgZ3JvdXAgYnkgY2F0ZWdvcnkgDQogICMgVGh1cywgb25seSBydW4gdGhlIGFuYWx5c2lzIGZvciBzZXgubWFsZSByYXRoZXIgdGhhbiBib3RoIHNleC5mZW1hbGUgQU5EIHNleC5tYWxlIHdoaWNoIHdvdWxkIGJlIHJlZHVuZGFudA0KICAjIHdlIHdpbGwgYWxzbyByZW1vdmUgYGFnZV9ncm91cC51bmtub3duYCBzaW5jZSB0aGVpciBpcyBvbmx5IDEgcGF0aWVudCBoZXJlDQogIGV4Y2x1ZGUgPC0gYygicmVhZG1pdHRlZC5mYWxzZSIsICJzZXguZmVtYWxlIiwgInNleC5vdGhlciIsICJzdXJ2aXZhbC5kZWNlYXNlZCIsICJzZXZlcml0eS5ub24tc2V2ZXJlIiwNCiAgICAgICAgICAgICAgICJjb3ZpZF9kaXNjaGFyZ2VkLmRpc2NoYXJnZWQiLCAiYWdlX2dyb3VwLlVua25vd24iKQ0KICB2YXJzIDwtIHZhcnNbIXZhcnMgJWluJSBleGNsdWRlXQ0KICANCiAgIyBjcmVhdGUgZW1wdHkgbGlzdCBmb3IgY2hpLnNxdWFyZSB0ZXN0IHJlc3VsdHMNCiAgY2hpX3Jlc3VsdF9saXN0ID0gbGlzdCgpDQogIA0KICAjIGZvciBlYWNoIHZhcmlhYmxlLCB3ZSB3aWxsIHJ1biB0aGUgY2hpLnNxdWFyZSB0ZXN0DQogIGZvcihpIGluIHZhcnMpIHsNCiAgICB0ZXN0ID0gY2FsY19jaGlzcShpLCB0YWJsZU9uZV9zdW1zLCBub25lX24sIHBuc19uLCBjbnNfbikNCiAgICBYMiA8LSByb3VuZCh0ZXN0JHN0YXRpc3RpYywgNCkNCiAgICBwX3ZhbHVlIDwtIHRlc3QkcC52YWx1ZQ0KICAgIGRmIDwtIHRlc3QkcGFyYW1ldGVyDQogICAgVmFyaWFibGUgPSBwYXN0ZShpKQ0KICAgIA0KICAgIGNoaV9yZXN1bHRzIDwtIGNiaW5kKFZhcmlhYmxlLCBYMiwgcF92YWx1ZSwgZGYpICU+JSANCiAgICAgIGRhdGEuZnJhbWUoKSAlPiUgDQogICAgICBtdXRhdGUocF92YWx1ZSA9IGFzLm51bWVyaWMocF92YWx1ZSkpDQogICAgDQogICAgcm93bmFtZXMoY2hpX3Jlc3VsdHMpIDwtIE5VTEwNCiAgICANCiAgICBjaGlfcmVzdWx0X2xpc3RbW2ldXSA8LSBjaGlfcmVzdWx0cw0KICB9DQogIA0KICAjIHNhdmUgbGlzdCBvZiBjaGkuc3F1YXJlIHRlc3QgcmVzdWx0cw0KICBjaGlzcV9yZXN1bHRzIDwtIGJpbmRfcm93cyhjaGlfcmVzdWx0X2xpc3QpDQogIA0KICByZXR1cm4oY2hpc3FfcmVzdWx0cykNCiAgDQogIA0KICB9DQpgYGANCg0KKipDb21iaW5lZCBBZHVsdCAmIFBlZGlhdHJpYyoqDQoNCmBgYHtyfQ0KY2hpc3FfY29tYmluZSA8LSBjb21wdXRlX2NoaV9zcXVhcmUodGFibGVvbmUgPSB0YWJsZW9uZV9jb21iaW5lICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoIWBUYWJsZSAxYCA9PSAnRGlzY2hhcmdlZCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAhYFRhYmxlIDFgID09ICdOb3QgRGlzY2hhcmdlZCcpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVtb190YWJsZSA9IGRlbW9fdGFibGVfY29tYmluZSAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKCF2YXJpYWJsZSA9PSAnY292aWRfZGlzY2hhcmdlZC5kaXNjaGFyZ2VkJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICF2YXJpYWJsZSA9PSAnY292aWRfZGlzY2hhcmdlZC5ub3QgZGlzY2hhcmdlZCcpKQ0KDQpkYXRhdGFibGUoY2hpc3FfY29tYmluZSAlPiUgYXJyYW5nZShwX3ZhbHVlKSkNCmBgYA0KDQoqKkFkdWx0KioNCg0KYGBge3J9DQpjaGlzcV9hZHVsdCA8LSBjb21wdXRlX2NoaV9zcXVhcmUodGFibGVvbmUgPSB0YWJsZW9uZV9hZHVsdCAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKCFgVGFibGUgMWAgPT0gJ0Rpc2NoYXJnZWQnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIWBUYWJsZSAxYCA9PSAnTm90IERpc2NoYXJnZWQnKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlbW9fdGFibGUgPSBkZW1vX3RhYmxlX2FkdWx0ICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoIXZhcmlhYmxlID09ICdjb3ZpZF9kaXNjaGFyZ2VkLmRpc2NoYXJnZWQnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIXZhcmlhYmxlID09ICdjb3ZpZF9kaXNjaGFyZ2VkLm5vdCBkaXNjaGFyZ2VkJykpDQoNCmRhdGF0YWJsZShjaGlzcV9hZHVsdCAlPiUgYXJyYW5nZShwX3ZhbHVlKSkNCmBgYA0KDQoqKlBlZGlhdHJpYyoqDQoNCmBgYHtyfQ0KY2hpc3FfcGVkaWF0cmljIDwtIGNvbXB1dGVfY2hpX3NxdWFyZSh0YWJsZW9uZSA9IHRhYmxlb25lX3BlZGlhdHJpYyAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKCFgVGFibGUgMWAgPT0gJ0Rpc2NoYXJnZWQnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIWBUYWJsZSAxYCA9PSAnTm90IERpc2NoYXJnZWQnKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlbW9fdGFibGUgPSBkZW1vX3RhYmxlX3BlZGlhdHJpYyAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKCF2YXJpYWJsZSA9PSAnY292aWRfZGlzY2hhcmdlZC5kaXNjaGFyZ2VkJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICF2YXJpYWJsZSA9PSAnY292aWRfZGlzY2hhcmdlZC5ub3QgZGlzY2hhcmdlZCcpKQ0KDQpkYXRhdGFibGUoY2hpc3FfcGVkaWF0cmljICU+JSBhcnJhbmdlKHBfdmFsdWUpKQ0KYGBgDQoNCioqMi4gQ29uZHVjdCBhbm92YSAoS3J1c2thbC1XYWxsaXMpIHRlc3RzIGZvciBjb250aW51b3VzIGNsaW5pY2FsIHZhcmlhYmxlcyoqDQoNCmBgYHtyfQ0KY29tcHV0ZV9rcnVza2FsIDwtIGZ1bmN0aW9uKGNsaW5pY2FsX3RhYmxlKSB7DQoNCiAgIyBjcmVhdGUgZW1wdHkgbGlzdCB0byBzYXZlIHByb2Nlc3NlZCB2YXJpYWJsZXMNCiAgcHJvY2Vzc2VkX2xpc3RfZmlncyA8LSBsaXN0KCkNCiAgDQogICMgY3JlYXRlIGEgbGlzdCBvZiBjbGluaWNhbCB2YXJpYWJsZXMgdG8gZnVydGhlciBwcmUtcHJvY2Vzcw0KICB2YXJzX3RvX3Byb2Nlc3MgPC0gdW5pcXVlKGNsaW5pY2FsX3RhYmxlJG5hbWUpDQogIA0KICAjIGZvciBlYWNoIHZhcmlhYmxlLCB3ZSB3aWxsIHJlbW92ZSB0aGUgW21pbiwgbWF4XSBhcyBiZWZvcmUNCiAgZm9yIChpIGluIHZhcnNfdG9fcHJvY2Vzcykgew0KICAgIG1vZF90YWJsZSA8LSBjbGluaWNhbF90YWJsZSAlPiUNCiAgICAgIHJlbmFtZSgidmFyIiA9IG5hbWUpICU+JQ0KICAgICAgZmlsdGVyKHZhciA9PSBpKSAlPiUNCiAgICAgIG11dGF0ZSgNCiAgICAgICAgc2l0ZSA9IHRvdXBwZXIoc2l0ZSksDQogICAgICAgIE5vbmUgPSBzdWIoIihcXCguKnxcXFsuKikiLCAiIiwgTm9uZSksDQogICAgICAgIFBlcmlwaGVyYWwgPSBzdWIoIihcXCguKnxcXFsuKikiLCAiIiwgUGVyaXBoZXJhbCksDQogICAgICAgIENlbnRyYWwgPSBzdWIoIihcXCguKnxcXFsuKikiLCAiIiwgQ2VudHJhbCkNCiAgICAgICkgJT4lDQogICAgICBtdXRhdGUoYWNyb3NzKE5vbmU6Q2VudHJhbCwgYXMubnVtZXJpYykpDQogIA0KICAgIHByb2Nlc3NlZF9saXN0X2ZpZ3NbW2ldXSA8LSBtb2RfdGFibGUNCiAgfQ0KICANCiAgY2xpbmljYWxfdGFibGVfYW5vdmEgPC0gYmluZF9yb3dzKHByb2Nlc3NlZF9saXN0X2ZpZ3MpDQogIA0KICAjIGZvcm1hdCB0aGUgY29udGludW91cyB2YXJpYWJsZXMNCiAgY29udF92YXJzID0gY2xpbmljYWxfdGFibGVfYW5vdmEgJT4lDQogICAgbXV0YXRlKA0KICAgICAgIyBjcmVhdGUgYSB2YXJpYWJsZSB3aXRob3V0IG1lYW4vbWVkaWFuIHByZWZpeA0KICAgICAgZ3JvdXBlZF92YXIgPSBnc3ViKCJNZWFuIHxNZWRpYW4gfCBcXFtNaW4sIE1heFxcXXwgXFwoU0RcXCkiLCAiIiwgdmFyKQ0KICAgICkgJT4lDQogICAgcGl2b3RfbG9uZ2VyKA0KICAgICAgY29scyA9IE5vbmU6Q2VudHJhbCwNCiAgICAgIG5hbWVzX3RvID0gInR5cGUiDQogICAgKSAlPiUNCiAgICBtdXRhdGUodHlwZSA9IGZhY3Rvcih0eXBlLCBsZXZlbHMgPSBjKCJOb25lIiwgIkNlbnRyYWwiLCAiUGVyaXBoZXJhbCIpKSkgJT4lIA0KICAgICMgc2VsZWN0IHZhcmlhYmxlcyB0byBhbmFseXplDQogICAgZmlsdGVyKGdyb3VwZWRfdmFyICVpbiUgYygiRWxpeGhhdXNlciBzY29yZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicHJlIGFkbWlzc2lvbiBjbnMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwcmUgYWRtaXNzaW9uIHBucyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInRpbWUgdG8gZGVhdGgiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0aW1lIHRvIGZpcnN0IGRpc2NoYXJnZSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInRpbWUgdG8gc2V2ZXJlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJudW1iZXIgb2YgcmVhZG1pc3Npb25zIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0aW1lIHRvIGZpcnN0IHJlYWRtaXNzaW9uIikpICU+JSANCiAgICBkaXN0aW5jdCgpDQogIA0KICAjIGNyZWF0ZSBmdW5jdGlvbiB0byBjb25kdWN0IGFub3ZhDQogIGNvbnRfdmFyX3Jlc3VsdHMgPC0gZnVuY3Rpb24odmFyaWFibGUpIHsNCiAgICANCiAgICBkZiA8LSBjb250X3ZhcnMgJT4lIA0KICAgIGZpbHRlcihncm91cGVkX3ZhciA9PSBwYXN0ZSh2YXJpYWJsZSkpDQogICAgDQogICAgIyBrcnVza2FsLnRlc3QgY2FsY3VsYXRlcyB0aGUga3J1c2thbC1XYWxsaXMgSC1zdGF0aXN0aWMgDQogICAgIyBkb2VzIG5vdCBhc3N1bWUgbm9ybWFsaXR5IGJldHdlZW4gZ3JvdXBzDQogICAgIyBhbHNvIGNhbGxlZCB0aGUgb25lLXdheSBBTk9WQSBvbiByYW5rcw0KICAgIHRlc3QgPSBrcnVza2FsLnRlc3QoZGYkdmFsdWUgfiBkZiR0eXBlKQ0KICAgIA0KICB9DQogIA0KICAjIGNyZWF0ZSBlbXB0eSBsaXN0IGZvciBLcnVza2FsLVdhbGxpcyByZXN1bHRzDQogIGNvbnRfdmFyc19saXN0ID0gbGlzdCgpDQogIA0KICAjIGNyZWF0ZSBsaXN0IG9mIHVuaXF1ZSB2YXJpYWJsZXMNCiAgb3V0Y29tZV9jb250X3ZhcnMgPSB1bmlxdWUoY29udF92YXJzJGdyb3VwZWRfdmFyKQ0KICANCiAgZm9yKGkgaW4gb3V0Y29tZV9jb250X3ZhcnMpIHsNCiAgICANCiAgICB0ZXN0ID0gY29udF92YXJfcmVzdWx0cyhpKQ0KICAgIFgyIDwtIHJvdW5kKHRlc3Qkc3RhdGlzdGljLCA0KQ0KICAgIHBfdmFsdWUgPC0gdGVzdCRwLnZhbHVlDQogICAgZGYgPC0gdGVzdCRwYXJhbWV0ZXINCiAgICBWYXJpYWJsZSA9IHBhc3RlKGkpDQogICAgDQogICAga3cgPC0gY2JpbmQoVmFyaWFibGUsIFgyLCBwX3ZhbHVlLCBkZikgJT4lIA0KICAgICAgZGF0YS5mcmFtZSgpIA0KICAgIA0KICAgIHJvd25hbWVzKGt3KSA8LSBOVUxMDQogICAgDQogICAgY29udF92YXJzX2xpc3RbW2ldXSA8LSBrdw0KICANCiAgICANCiAgfQ0KICANCiAgYW5vdmFfcmVzdWx0cyA8LSBiaW5kX3Jvd3MoY29udF92YXJzX2xpc3QpDQogIA0KICB9DQpgYGANCg0KKipBZHVsdCAmIFBlZGlhdHJpYyBLcnVza2FsbC1XYWxsaXMqKg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KDQprd19jb21iaW5lIDwtIGNvbXB1dGVfa3J1c2thbChjbGluaWNhbF90YWJsZSA9IGNsaW5pY2FsX3RhYmxlX2NvbWJpbmUpDQpkYXRhdGFibGUoa3dfY29tYmluZSAlPiUgYXJyYW5nZShwX3ZhbHVlKSkNCmBgYA0KDQoqKkFkdWx0IENsaW5pY2FsIEtydXNrYWxsLVdhbGxpcyoqDQoNCipOb3RlOiBtZWRpYW4gQ05TIGlzIDAgZm9yIGFsbCBzaXRlcyAocmVhc29uIGZvciB0aGUgTkEgdmFsdWVzKSoNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCg0Ka3dfYWR1bHQgPC0gY29tcHV0ZV9rcnVza2FsKGNsaW5pY2FsX3RhYmxlID0gY2xpbmljYWxfdGFibGVfYWR1bHQpDQpkYXRhdGFibGUoa3dfYWR1bHQgJT4lIGFycmFuZ2UocF92YWx1ZSkpDQpgYGANCg0KKipQZWRpYXRyaWMgS3J1c2thbGwtV2FsbGlzKioNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCg0Ka3dfcGVkaWF0cmljIDwtIGNvbXB1dGVfa3J1c2thbChjbGluaWNhbF90YWJsZSA9IGNsaW5pY2FsX3RhYmxlX3BlZGlhdHJpYykNCmRhdGF0YWJsZShrd19wZWRpYXRyaWMgJT4lIGFycmFuZ2UocF92YWx1ZSkpDQpgYGANCg0KKiozLiBDb21iaW5lIGNoaS1zcXVhcmUgYW5kIEtydXNrYWwtV2FsbGlzIHJlc3VsdHMqKg0KDQpXZSB3aWxsIGV2YWx1YXRlIHNpZ25pZmljYW5jZSB3aGVuIGNvbnRyb2xsaW5nIGZvciBGYWxzZSBEaXNjb3ZlcnkgUmF0ZSAoRkRSKSANCg0KYGBge3J9DQp0YWJsZW9uZV9zdGF0cyA8LSBmdW5jdGlvbihjaGlzcV9yZXN1bHRzLCBhbm92YV9yZXN1bHRzLCBwb3B1bGF0aW9uKSB7DQoNCiAgZGVtb19zdGF0cyA8LSByYmluZChjaGlzcV9yZXN1bHRzICU+JSBzZWxlY3QoVmFyaWFibGUsIHBfdmFsdWUpLA0KICAgICAgICAgICAgICAgICAgICAgIGFub3ZhX3Jlc3VsdHMgJT4lIHNlbGVjdChWYXJpYWJsZSwgcF92YWx1ZSkpDQogIA0KICAjIGFkanVzdCBwLXZhbHVlcyB3aXRoIEZEUg0KICBkZW1vX3N0YXRzJGFkal9wX3ZhbHVlIDwtIHAuYWRqdXN0KGRlbW9fc3RhdHMkcF92YWx1ZSwgbWV0aG9kID0gImZkciIpDQogIA0KICAjIHJvdW5kICYgZm9ybWF0IHAtdmFsdWUgDQogIGRlbW9fc3RhdHMgPC0gZGVtb19zdGF0cyAlPiUgDQogICAgbXV0YXRlKHBfdmFsdWUgPSBhcy5udW1lcmljKHBfdmFsdWUpLA0KICAgICAgICAgICBwdmFsdWUgPSBpZl9lbHNlKHBfdmFsdWUgPCAwLjAwMSwgIjwgMC4wMDEiLCBwYXN0ZShyb3VuZChwX3ZhbHVlLCAzKSkpLA0KICAgICAgICAgICBhZGoucF92YWx1ZSA9IGlmX2Vsc2UoYWRqX3BfdmFsdWUgPCAwLjAwMSwgIjwgMC4wMDEiLCBwYXN0ZShyb3VuZChhZGpfcF92YWx1ZSwgMykpKSkNCiAgDQogICMgdGlkeSB1cCB0aGUgZGVtb2dyYXBoaWNzIHN0YXQgdGFibGUNCiAgZGVtb19zdGF0c190aWR5IDwtIGRlbW9fc3RhdHMgJT4lIA0KICAgIHJlbmFtZShgVW5hZGp1c3RlZCBQLXZhbHVlIChyYXcpYCA9IHBfdmFsdWUsDQogICAgICAgICAgIGBGRFIgQWRqdXN0ZWQgUC12YWx1ZSAocmF3KWAgPSBhZGpfcF92YWx1ZSwNCiAgICAgICAgICAgYFVuYWRqdXN0ZWQgUC12YWx1ZSAodGlkeSlgID0gcHZhbHVlLA0KICAgICAgICAgICBgRkRSIEFkanVzdGVkIFAtdmFsdWUgKHRpZHkpYCA9IGFkai5wX3ZhbHVlKSAlPiUgDQogICAgc2VsZWN0KFZhcmlhYmxlLCBgVW5hZGp1c3RlZCBQLXZhbHVlIChyYXcpYCwgYEZEUiBBZGp1c3RlZCBQLXZhbHVlIChyYXcpYCwgYFVuYWRqdXN0ZWQgUC12YWx1ZSAodGlkeSlgLCBgRkRSIEFkanVzdGVkIFAtdmFsdWUgKHRpZHkpYCkNCg0KICB3cml0ZS5jc3YoZGVtb19zdGF0c190aWR5LCBwYXN0ZTAoInRhYmxlcy9UYWJsZTFfcHZhbHNfIiwgcG9wdWxhdGlvbiwgIi5jc3YiKSwgcm93Lm5hbWVzID0gRkFMU0UpDQogIA0KICByZXR1cm4oZGVtb19zdGF0c190aWR5KQ0KICANCn0NCmBgYA0KDQoqKkluY29ycG9yYXRlIENvbW9yYmlkaXRpZXMqKg0KDQpgYGB7cn0NCiMgZGVmaW5lIG1hdHJpeCBmb3IgZWFjaCBjb21vcmJpZGl0eQ0KY29tb3JiX21hdHJpeCA8LSBmdW5jdGlvbihjb21vcmJpZGl0eV90YWJsZSwgY29tb3JiaWRpdHksIHBvcHVsYXRpb24pIHsNCg0KICAgIGlmKHBvcHVsYXRpb249PSdDb21iaW5lZCcpIHsNCiAgICAgIGNvbW9yYmlkaXR5X3RhYmxlID0gY29tb3JiaWRpdHlfdGFibGUNCiAgICB9IGVsc2UgaWYgKHBvcHVsYXRpb249PSJBZHVsdCIpIHsNCiAgICAgIGNvbW9yYmlkaXR5X3RhYmxlID0gY29tb3JiaWRpdHlfdGFibGUgJT4lIA0KICAgICAgICBmaWx0ZXIocG9wdWxhdGlvbiA9PSAiQWR1bHQiKQ0KICAgIH0gZWxzZSBpZiAocG9wdWxhdGlvbj09IlBlZGlhdHJpYyIpIHsNCiAgICAgIGNvbW9yYmlkaXR5X3RhYmxlID0gY29tb3JiaWRpdHlfdGFibGUgJT4lIA0KICAgICAgICBmaWx0ZXIocG9wdWxhdGlvbiA9PSAiUGVkaWF0cmljIikNCiAgICB9IGVsc2Ugew0KICAgICAgcHJpbnQoJ2luY29ycmVjdCBwb3B1bGF0aW9uIHNwZWNpZmljYXRpb24nKQ0KICAgIH0NCiAgDQogIG1hdF9jb21vcmIgPC0gY29tb3JiaWRpdHlfdGFibGUgJT4lDQogICAgZmlsdGVyKENvbW9yYmlkaXR5ID09IHBhc3RlKGNvbW9yYmlkaXR5KSkgJT4lDQogICAgZ3JvdXBfYnkoQ29tb3JiaWRpdHkpICU+JSANCiAgICBtdXRhdGUoTm9uZV9jb21vcmIgPSBzdW0oTm9uZV9Ub3RhbCwgbmEucm0gPSBUUlVFKSwNCiAgICBDTlNfY29tb3JiID0gc3VtKENOU19Ub3RhbCwgbmEucm0gPSBUUlVFKSwNCiAgICBQTlNfY29tb3JiID0gc3VtKFBOU19Ub3RhbCwgbmEucm0gPSBUUlVFKSkgJT4lDQogICAgdW5ncm91cCgpICU+JQ0KICAgIGRpc3RpbmN0KE5vbmVfY29tb3JiLCBDTlNfY29tb3JiLCBQTlNfY29tb3JiKSAlPiUNCiAgICBtdXRhdGUobm9uZV9kaWYgPSBuZXVyb19wdF9jb3VudHNfc3VtJE5vbmVfbiAtIE5vbmVfY29tb3JiLA0KICAgIHBuc19kaWYgPSBuZXVyb19wdF9jb3VudHNfc3VtJFBOU19uIC0gUE5TX2NvbW9yYiwNCiAgICBjbnNfZGlmID0gbmV1cm9fcHRfY291bnRzX3N1bSRDTlNfbiAtIENOU19jb21vcmIpICU+JQ0KICAgIGRhdGEuZnJhbWUoKSAlPiUNCiAgICAjYXMuaW50ZWdlcigpICU+JQ0KICAgIG1hdHJpeChucm93ID0gMiwgbmNvbCA9IDMsIGJ5cm93ID0gVFJVRSkNCg0KICBjb21vcmJfcmVzdWx0cyA8LSBjaGlzcS50ZXN0KHVubGlzdChtYXRfY29tb3JiKSkNCiAgDQogIHJlc3VsdHNfZGYgPC0gZGF0YS5mcmFtZShWYXJpYWJsZSA9IHBhc3RlKGNvbW9yYmlkaXR5KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgWDIgPSByb3VuZChjb21vcmJfcmVzdWx0cyRzdGF0aXN0aWMsIDQpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwX3ZhbHVlID0gY29tb3JiX3Jlc3VsdHMkcC52YWx1ZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGYgPSBjb21vcmJfcmVzdWx0cyRwYXJhbWV0ZXIpDQogIHJvdy5uYW1lcyhyZXN1bHRzX2RmKSA8LSBOVUxMDQogIA0KICByZXR1cm4ocmVzdWx0c19kZikNCn0NCg0KIyMgY29tYmluZWQgY29tb3JiaWRpdGllcw0KY29tb3JiaWRpdHlfY29tYmluZWRfY2hpX2xpc3QgPC0gbGlzdCgpDQoNCmZvcihpIGluIHRvcF9jb21vcmJfYWR1bHRzKSB7DQogIA0KICBjb21vcmJfY2hpX3Jlc3VsdHMgPC0gY29tb3JiX21hdHJpeChjb21vcmJpZGl0eV90YWJsZSA9IGNvbW9yYl90YWJsZV93aWRlLCBpLCBwb3B1bGF0aW9uID0gIkNvbWJpbmVkIikNCiAgY29tb3JiaWRpdHlfY29tYmluZWRfY2hpX2xpc3RbW2ldXSA8LSBjb21vcmJfY2hpX3Jlc3VsdHMNCn0NCg0KY29tb3JiaWRpdHlfY29tYmluZWRfY2hpIDwtIGNvbW9yYmlkaXR5X2NvbWJpbmVkX2NoaV9saXN0ICU+JQ0KICBiaW5kX3Jvd3MoKSANCg0KY2hpc3FfY29tYmluZV9jb21vcmIgPC0gY2hpc3FfY29tYmluZSAlPiUNCiAgbXV0YXRlKFgyID0gYXMubnVtZXJpYyhYMiksDQogIGRmID0gYXMubnVtZXJpYyhkZikpICU+JQ0KICBiaW5kX3Jvd3MoY29tb3JiaWRpdHlfY29tYmluZWRfY2hpKSANCg0KDQoNCiMjIGFkdWx0IGNvbW9yYmlkaXRpZXMNCmNvbW9yYmlkaXR5X2FkdWx0X2NoaV9saXN0IDwtIGxpc3QoKQ0KDQpmb3IoaSBpbiB0b3BfY29tb3JiX2FkdWx0cykgew0KICANCiAgY29tb3JiX2NoaV9yZXN1bHRzIDwtIGNvbW9yYl9tYXRyaXgoY29tb3JiaWRpdHlfdGFibGUgPSBjb21vcmJfdGFibGVfd2lkZSwgaSwgcG9wdWxhdGlvbiA9ICJBZHVsdCIpDQogIGNvbW9yYmlkaXR5X2FkdWx0X2NoaV9saXN0W1tpXV0gPC0gY29tb3JiX2NoaV9yZXN1bHRzDQp9DQoNCmNvbW9yYmlkaXR5X2FkdWx0X2NoaSA8LSBjb21vcmJpZGl0eV9hZHVsdF9jaGlfbGlzdCAlPiUNCiAgYmluZF9yb3dzKCkgDQoNCmNoaXNxX2FkdWx0X2NvbW9yYiA8LSBjaGlzcV9hZHVsdCAlPiUNCiAgbXV0YXRlKFgyID0gYXMubnVtZXJpYyhYMiksDQogIGRmID0gYXMubnVtZXJpYyhkZikpICU+JQ0KICBiaW5kX3Jvd3MoY29tb3JiaWRpdHlfYWR1bHRfY2hpKSANCg0KIyMgcGVkaWF0cmljIGNvbW9yYmlkaXRpZXMNCmNvbW9yYmlkaXR5X3BlZGlhdHJpY19jaGlfbGlzdCA8LSBsaXN0KCkNCg0KZm9yKGkgaW4gdG9wX2NvbW9yYl9wZWRpYXRyaWNzKSB7DQogIA0KICBjb21vcmJfY2hpX3Jlc3VsdHMgPC0gY29tb3JiX21hdHJpeChjb21vcmJpZGl0eV90YWJsZSA9IGNvbW9yYl90YWJsZV93aWRlLCBpLCBwb3B1bGF0aW9uID0gIlBlZGlhdHJpYyIpDQogIGNvbW9yYmlkaXR5X3BlZGlhdHJpY19jaGlfbGlzdFtbaV1dIDwtIGNvbW9yYl9jaGlfcmVzdWx0cw0KfQ0KDQpjb21vcmJpZGl0eV9wZWRpYXRyaWNfY2hpIDwtIGNvbW9yYmlkaXR5X3BlZGlhdHJpY19jaGlfbGlzdCAlPiUNCiAgYmluZF9yb3dzKCkgDQoNCmNoaXNxX3BlZGlhdHJpY19jb21vcmIgPC0gY2hpc3FfcGVkaWF0cmljICU+JQ0KICBtdXRhdGUoWDIgPSBhcy5udW1lcmljKFgyKSwNCiAgZGYgPSBhcy5udW1lcmljKGRmKSkgJT4lDQogIGJpbmRfcm93cyhjb21vcmJpZGl0eV9wZWRpYXRyaWNfY2hpX2xpc3QpIA0KYGBgDQoNCiMjIFRhYmxlIE9uZSAtIENvbWJpbmVkIEFuYWx5c2lzDQoNCmBgYHtyfQ0KY29tYmluZV90YWJsZW9uZV9zdGF0cyA8LSB0YWJsZW9uZV9zdGF0cyhjaGlzcV9yZXN1bHRzID0gY2hpc3FfY29tYmluZV9jb21vcmIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFub3ZhX3Jlc3VsdHMgPSBrd19jb21iaW5lLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3B1bGF0aW9uID0gImNvbWJpbmVkIikgDQogIA0KDQpkYXRhdGFibGUoY29tYmluZV90YWJsZW9uZV9zdGF0cykNCmBgYA0KDQoNCiMjIFRhYmxlIE9uZSAtIEFkdWx0IEFuYWx5c2lzDQoNCmBgYHtyfQ0KYWR1bHRfdGFibGVvbmVfc3RhdHMgPC0gdGFibGVvbmVfc3RhdHMoY2hpc3FfcmVzdWx0cyA9IGNoaXNxX2FkdWx0X2NvbW9yYiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYW5vdmFfcmVzdWx0cyA9IGt3X2FkdWx0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3B1bGF0aW9uID0gImFkdWx0IikgDQogIA0KDQpkYXRhdGFibGUoYWR1bHRfdGFibGVvbmVfc3RhdHMpDQpgYGANCg0KIyMgVGFibGUgT25lIC0gUGVkaWF0cmljIEFuYWx5c2lzDQoNCmBgYHtyfQ0KcGVkaWF0cmljX3RhYmxlb25lX3N0YXRzIDwtIHRhYmxlb25lX3N0YXRzKGNoaXNxX3Jlc3VsdHMgPSBjaGlzcV9wZWRpYXRyaWNfY29tb3JiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbm92YV9yZXN1bHRzID0ga3dfcGVkaWF0cmljLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3B1bGF0aW9uID0gInBlZGlhdHJpYyIpIA0KICANCg0KZGF0YXRhYmxlKHBlZGlhdHJpY190YWJsZW9uZV9zdGF0cykNCmBgYA0KDQojICoqTmV1cm9sb2dpY2FsIERpYWdub3NpcyBBbmFseXNpcyoqDQoNCkV2YWx1YXRlIENOUyBhbmQgUE5TIG5ldXJvbG9naWNhbCBkaWFnbm9zZXMgaW4gb3VyIHBhdGllbnQgY29ob3J0DQoNCioqRm9ybWF0IERpYWdub3N0aWMgY29kZSBkYXRhKioNCg0KYGBge3IgZmlnLndpZHRoPTEyfQ0KIyBpbXBvcnQgbGlzdCBvZiBJQ0QtOS9JQ0QtMTAgY29kZXMNCm5ldXJvX2ljZHNfMTAgPC0NCiAgcmVhZHhsOjpyZWFkX2V4Y2VsKCJwdWJsaWMtZGF0YS8yMDIwLTAyLTIyX25ldXJvLWljZDEwX0NOU3ZQTlMueGxzeCIsIHNoZWV0ID0gMikgJT4lIA0KICByZW5hbWUoDQogICAgImljZCIgPSBgSUNELTEwYCwNCiAgICAicG5zX2NucyIgPSBgTmVydm91cyBzeXN0ZW0gSW52b2x2ZW1lbnQgKDE9Y2VudHJhbCwgMj1wZXJpcGhlcmFsLCAzPWlycmVsZXZhbnQpYCwNCiAgICAidHlwZSIgPSBgSUNELTEwX3R5cGUgKDE9Zmlyc3QgdGhyZWUgYWxwaGFudW1lcmljIGNvZGUgb25seSwgMj1kaWdpdHMgYWZ0ZXIgZGVjaW1hbCBwb2ludClgKSAlPiUNCiAgZmlsdGVyKHR5cGUgPT0gMSkgJT4lIA0KICBtdXRhdGUocG5zX2NucyA9IGFzLmZhY3RvcihwbnNfY25zKSAlPiUgZmN0X3JlY29kZSgNCiAgICBDZW50cmFsID0gIjEiLA0KICAgIFBlcmlwaGVyYWwgPSAiMiINCiAgKSkgJT4lDQogIGRpc3RpbmN0KGljZCwgYE5ldXJvbG9naWNhbCBEaXNlYXNlIENhdGVnb3J5YCwgcG5zX2NucywgYElDRC0xMCBEZXNjcmlwdGlvbiBSZXZpc2VkYCkgJT4lDQogIHJlbmFtZSgiZGVzY3JpcHRpb24iID0gYElDRC0xMCBEZXNjcmlwdGlvbiBSZXZpc2VkYCkgJT4lDQogIG11dGF0ZShjb25jZXB0X3R5cGUgPSAiaWNkLTEwIikNCg0KbmV1cm9faWNkc185IDwtIHJlYWQuY3N2KCJwdWJsaWMtZGF0YS9pY2Q5X3RhYl9DTlN2UE5TLmNzdiIpICU+JQ0KICByZW5hbWUoDQogICAgIk5ldXJvbG9naWNhbCBEaXNlYXNlIENhdGVnb3J5IiA9ICJOZXVyb2xvZ2ljYWwuRGlzZWFzZS5DYXRlZ29yeSIsDQogICAgInBuc19jbnMiID0gYE5lcnZvdXMuc3lzdGVtLkludm9sdmVtZW50Li4xLmNlbnRyYWwuLjIucGVyaXBoZXJhbC5gLA0KICAgICJpY2RfZGVzY3JpcHRpb24iID0gYGljZDlfZGVzY19yZXZpc2VkYA0KICApICU+JQ0KICBtdXRhdGUoDQogICAgcG5zX2NucyA9IGFzLmZhY3RvcihwbnNfY25zKSAlPiUgZmN0X3JlY29kZSgNCiAgICAgIENlbnRyYWwgPSAiMSIsDQogICAgICBQZXJpcGhlcmFsID0gIjIiDQogICAgKSwNCiAgICBjb25jZXB0X3R5cGUgPSAiRElBRy1JQ0Q5Ig0KICApICU+JQ0KICBzZWxlY3QoaWNkLCBgTmV1cm9sb2dpY2FsIERpc2Vhc2UgQ2F0ZWdvcnlgLCBwbnNfY25zLCBpY2RfZGVzY3JpcHRpb24pICU+JQ0KICByZW5hbWUoImRlc2NyaXB0aW9uIiA9IGljZF9kZXNjcmlwdGlvbikgJT4lDQogIG11dGF0ZShjb25jZXB0X3R5cGUgPSAiaWNkLTkiKQ0KDQojIGJpbmQgbGlzdCBvZiBJQ0QtOS9JQ0QtMTAgY29kZXMNCmljZHMgPC0gcmJpbmQobmV1cm9faWNkc18xMCwgbmV1cm9faWNkc185KQ0KYGBgDQoNCioqQ2FsY3VsYXRlIHRvdGFsIG51bWJlciBvZiBuZXVybyBkaWFnbm9zZXMgZm9yIGVhY2ggYWR1bHRzICYgcGVkaWF0cmljcyoqDQoNCmBgYHtyfQ0KY29tcHV0ZV9uZXVyb19jb3VudHMgPC0gZnVuY3Rpb24oc29ydGVkX3NpdGVzLCBzYW1wbGVfc2l6ZSwgaXNfcGVkaWF0cmljID0gRkFMU0UpIHsNCiAgDQogICAgIyBjcmVhdGUgZW1wdHkgbGlzdCB0byBzYXZlIHByb2Nlc3NlZCBkaWFnbm9zdGljIGRhdGENCiAgICBkaWFnX3RhYmxlX2xpc3QgPC0gbGlzdCgpDQogIA0KICAgIGlmKGlzX3BlZGlhdHJpYz09RkFMU0UpIHsNCiAgICAgIHBvcHVsYXRpb24gPSAiYWR1bHRzIg0KICAgICAgfSBlbHNlIHsNCiAgICAgIHBvcHVsYXRpb24gPSAicGVkaWF0cmljcyINCiAgICAgIH0NCiAgDQogICMgZm9yIGVhY2ggaGVhbHRoY2FyZSBzeXN0ZW0sIHdlIHdpbGwgY2FsY3VsYXRlIHRoZSB0b3RhbCBudW1iZXIgb2YgcGF0aWVudHMgd2l0aCBlYWNoIElDRCBjb2RlDQogIGZvciAoaSBpbiBzb3J0ZWRfc2l0ZXMpIHsNCiAgICB0bXAgPC0gZ2V0KGkpDQogICAgdG1wX2RpYWcgPC0gdG1wW1tjKA0KICAgICAgImZpcnN0X2hvc3BfcmVzdWx0cyIsDQogICAgICAiaWNkX3RhYmxlcyIsDQogICAgICBwYXN0ZTAoImljZF90YWJsZXNfIiwgcG9wdWxhdGlvbiksDQogICAgICAiZGVtb190YWJsZSINCiAgICApXV0NCiAgICB0bXBfZGlhZyRzaXRlIDwtIHRtcFtbInNpdGUiXV0NCiAgICBkaWFnX3RhYmxlX2xpc3RbW2ldXSA8LSB0bXBfZGlhZyAlPiUNCiAgICAgIGZpbHRlcih2YXJpYWJsZSA9PSAic2V4LmZlbWFsZSIgfCB2YXJpYWJsZSA9PSAic2V4Lm1hbGUiIHwgdmFyaWFibGUgPT0gInNleC5vdGhlciIpICU+JQ0KICAgICAgc2VsZWN0KHNpdGUsIGNvbnRhaW5zKCJuX3ZhciIpKQ0KICB9DQogIA0KICBkaWFnX3RhYmxlX3dpZGUgPC0gYmluZF9yb3dzKGRpYWdfdGFibGVfbGlzdCkNCiAgDQogICMgd2Ugd2lsbCBjb3VudCB0aGUgdG90YWwgbnVtYmVyIGFuZCBwZXJjZW50IGFjcm9zcyBoZWFsdGhjYXJlIHN5c3RlbXMNCiAgZGlhZ19jb3VudHMgPC0gY29sU3VtcyhGaWx0ZXIoaXMubnVtZXJpYywgZGlhZ190YWJsZV93aWRlKSwgbmEucm0gPSBUUlVFKSAlPiUgDQogICAgZGF0YS5mcmFtZSgpICU+JSANCiAgICBtdXRhdGUocGVyYyA9IHJvdW5kKC4vc2FtcGxlX3NpemUgKiAxMDAsIDEpKQ0KICANCiAgIyBmb3JtYXQgZGlhZ25vc3RpYyB0YWJsZQ0KICBkaWFnX2NvdW50cyA8LSB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbihkaWFnX2NvdW50cywgImljZCIpICU+JQ0KICAgIG11dGF0ZShpY2QgPSBnc3ViKCJuX3Zhcl8iLCAiIiwgaWNkKSkgJT4lDQogICAgcmVuYW1lKGBOdW1iZXIgb2YgUGF0aWVudHNgID0gIi4iKSAlPiUNCiAgICBmdWxsX2pvaW4oLiwgaWNkcywgYnkgPSAiaWNkIikgJT4lDQogICAgZHBseXI6OmFycmFuZ2UoZGVzYyhgTnVtYmVyIG9mIFBhdGllbnRzYCkpICU+JSANCiAgICBtdXRhdGUoYE51bWJlciBvZiBQYXRpZW50cyAoJSBvZiBDb2hvcnQpYCA9IHBhc3RlKGBOdW1iZXIgb2YgUGF0aWVudHNgLCAiKCIsIHBlcmMsICIpIiwgc2VwID0gIiAiKSkgJT4lIA0KICAgIHNlbGVjdChwbnNfY25zLCBgTmV1cm9sb2dpY2FsIERpc2Vhc2UgQ2F0ZWdvcnlgLCBjb25jZXB0X3R5cGUsIA0KICAgICAgICAgICBpY2QsIGRlc2NyaXB0aW9uLCBgTnVtYmVyIG9mIFBhdGllbnRzICglIG9mIENvaG9ydClgKSAlPiUgDQogICAgICByZW5hbWUoQ29kZSA9ICJpY2QiLA0KICAgICAgICAgICBgTmVydm91cyBTeXN0ZW1gID0gInBuc19jbnMiLA0KICAgICAgICAgICBEZXNjcmlwdGlvbiA9ICJkZXNjcmlwdGlvbiIsDQogICAgICAgICAgIGBJQ0QgVmVyc2lvbmAgPSAiY29uY2VwdF90eXBlIikgJT4lIA0KICAgIG11dGF0ZShgTnVtYmVyIG9mIFBhdGllbnRzICglIG9mIENvaG9ydClgID0gaWZfZWxzZShgTnVtYmVyIG9mIFBhdGllbnRzICglIG9mIENvaG9ydClgID09ICJOQSAoIE5BICkiLCAiMCAoIDAgKSIsIGBOdW1iZXIgb2YgUGF0aWVudHMgKCUgb2YgQ29ob3J0KWApLA0KICAgICAgICAgICBDb2RlID0gaWZfZWxzZShDb2RlID09ICJOTiIsICJObyBOZXVyb2xvZ2ljYWwgRGlzZWFzZSAoTk5DKSIsIENvZGUpKQ0KICANCiAgcmV0dXJuKGRpYWdfY291bnRzKQ0KICANCiAgfQ0KDQpgYGANCg0KYGBge3J9DQpuZXVyb19jb3VudHNfYWR1bHQgPC0gY29tcHV0ZV9uZXVyb19jb3VudHMoc29ydGVkX3NpdGVzID0gYWR1bHRfc2l0ZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlX3NpemUgPSBuX2FkdWx0LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpc19wZWRpYXRyaWMgPSBGQUxTRSkNCg0KbmV1cm9fY291bnRzX3BlZGlhdHJpYyA8LSBjb21wdXRlX25ldXJvX2NvdW50cyhzb3J0ZWRfc2l0ZXMgPSBwZWRpYXRyaWNfc2l0ZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlX3NpemUgPSBuX3BlZGlhdHJpYywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaXNfcGVkaWF0cmljID0gVFJVRSkNCg0KbmV1cm9fY291bnRzX2NvbWJpbmVkIDwtIG5ldXJvX2NvdW50c19hZHVsdCAlPiUgDQogIHJlbmFtZShgTnVtYmVyIG9mIEFkdWx0IFBhdGllbnRzICglIG9mIENvaG9ydClgID0gYE51bWJlciBvZiBQYXRpZW50cyAoJSBvZiBDb2hvcnQpYCkgJT4lIA0KICBsZWZ0X2pvaW4oLiwgbmV1cm9fY291bnRzX3BlZGlhdHJpYyAlPiUgDQogICAgICAgICAgICAgIHNlbGVjdChgSUNEIFZlcnNpb25gLCBDb2RlLCBgTnVtYmVyIG9mIFBhdGllbnRzICglIG9mIENvaG9ydClgKSAlPiUgDQogICAgICAgICAgICAgIHJlbmFtZSgiTnVtYmVyIG9mIFBlZGlhdHJpYyBQYXRpZW50cyAoJSBvZiBDb2hvcnRgKSIgPSBgTnVtYmVyIG9mIFBhdGllbnRzICglIG9mIENvaG9ydClgKSkNCg0Kd3JpdGUuY3N2KG5ldXJvX2NvdW50c19jb21iaW5lZCwgcGFzdGUwKCJ0YWJsZXMvU3VwcC5UYWJsZTJfZGlhZ25vc2VzIiwgIi5jc3YiKSwgcm93Lm5hbWVzID0gRkFMU0UpDQogIA0KYGBgDQoNCmBgYHtyfQ0KZGF0YXRhYmxlKG5ldXJvX2NvdW50c19hZHVsdCwgY2FwdGlvbiA9ICJOdW1iZXIgKCUpIG9mIEFkdWx0IFBhdGllbnRzIHdpdGggZWFjaCBOZXVyb2xvZ2ljYWwgRGlhZ25vc2lzIikgDQpgYGANCg0KYGBge3J9DQpkYXRhdGFibGUobmV1cm9fY291bnRzX3BlZGlhdHJpYywgY2FwdGlvbiA9ICJOdW1iZXIgKCUpIG9mIFBlZGlhdHJpYyBQYXRpZW50cyB3aXRoIGVhY2ggTmV1cm9sb2dpY2FsIERpYWdub3NpcyIpIA0KYGBgDQoNCiMjIEV2YWx1YXRlIERpYWdub3NlcyBieSBBZ2UgJiBPdXRjb21lDQoNCmBgYHtyfQ0KY29tcHV0ZV9zdHJhdGlmaWVkX25ldXJvX2NvdW50cyA8LSBmdW5jdGlvbih0YWJsZW9uZSwgc29ydGVkX3NpdGVzLCBpc19wZWRpYXRyaWMgPSBGQUxTRSkgew0KICANCiAgIyBjcmVhdGUgZW1wdHkgbGlzdCB0byBzYXZlIHByb2Nlc3NlZCBkaWFnbm9zdGljIGRhdGENCiAgICBkaWFnX3RhYmxlX2xpc3QgPC0gbGlzdCgpDQogIA0KICAgIGlmKGlzX3BlZGlhdHJpYz09RkFMU0UpIHsNCiAgICAgIHBvcHVsYXRpb24gPSAiYWR1bHRzIg0KICAgICAgfSBlbHNlIHsNCiAgICAgIHBvcHVsYXRpb24gPSAicGVkaWF0cmljcyINCiAgICAgIH0NCiAgDQogICMgZm9yIGVhY2ggaGVhbHRoY2FyZSBzeXN0ZW0sIHdlIHdpbGwgY2FsY3VsYXRlIHRoZSB0b3RhbCBudW1iZXIgb2YgcGF0aWVudHMgd2l0aCBlYWNoIElDRCBjb2RlDQogIGZvciAoaSBpbiBzb3J0ZWRfc2l0ZXMpIHsNCiAgICAjcHJpbnQoaSkNCiAgICB0bXAgPC0gZ2V0KGkpDQogICAgdG1wX2RpYWcgPC0gdG1wW1tjKA0KICAgICAgImZpcnN0X2hvc3BfcmVzdWx0cyIsDQogICAgICAiaWNkX3RhYmxlcyIsDQogICAgICBwYXN0ZTAoImljZF90YWJsZXNfIiwgcG9wdWxhdGlvbiksDQogICAgICAiZGVtb190YWJsZSINCiAgICApXV0NCiAgICB0bXBfZGlhZyRzaXRlIDwtIHRtcFtbInNpdGUiXV0NCiAgICB0cnkoDQogICAgZGlhZ190YWJsZV9saXN0W1tpXV0gPC0gdG1wX2RpYWcgJT4lDQogICAgICBzZWxlY3Qoc2l0ZSwgdmFyaWFibGUsIHN0YXJ0c193aXRoKCJuX3ZhciIpKSAlPiUgDQogICAgICBwaXZvdF9sb25nZXIoY29scyA9IHN0YXJ0c193aXRoKCJuX3ZhciIpLCBuYW1lc190byA9ICJjb2RlIiwgdmFsdWVzX3RvID0gImZyZXEiKSAlPiUgDQogICAgICB1bmdyb3VwKCkNCiAgICApDQogIH0NCg0KICBkaWFnX3RhYmxlX3dpZGVfYWxsIDwtIGJpbmRfcm93cyhkaWFnX3RhYmxlX2xpc3QpICU+JSANCiAgICAgIGdyb3VwX2J5KHZhcmlhYmxlLCBjb2RlKSAlPiUgDQogICAgIyB0b3RhbCBudW1iZXIgb2YgcGF0aWVudHMgd2hvIGhhZCBlYWNoIGNvZGUgZm9yIGVhY2ggdmFyaWFibGUgYWNyb3NzIHNpdGVzDQogICAgICBtdXRhdGUodG90YWxfbl92YXIgID0gc3VtKGZyZXEsIG5hLnJtID0gVFJVRSkpICU+JSANCiAgICBkaXN0aW5jdCh2YXJpYWJsZSwgY29kZSwgdG90YWxfbl92YXIpICU+JSANCiAgICBtdXRhdGUoY29kZSA9IGdzdWIoIm5fdmFyXyIsICIiLCBjb2RlKSwNCiAgICAgICAgICAgdmFyaWFibGUgPSBnc3ViKCJTZXZlcml0eS4iLCAiIiwgdmFyaWFibGUpLA0KICAgICAgICAgICB2YXJpYWJsZSA9IGdzdWIoIlN1cnZpdmFsLiIsICIiLCB2YXJpYWJsZSksDQogICAgICAgICAgIHZhcmlhYmxlID0gZ3N1YigicmVhZG1pdHRlZCIsICIiLCB2YXJpYWJsZSksDQogICAgICAgICAgIHZhcmlhYmxlID0gZ3N1YigiYWdlX2dyb3VwIiwgIiIsIHZhcmlhYmxlKSwNCiAgICAgICAgICAgdmFyaWFibGUgPSBnc3ViKCJjb3ZpZF9kaXNjaGFyZ2VkIiwgIiIsIHZhcmlhYmxlKSwNCiAgICAgICAgICAgdmFyaWFibGUgPSBpZl9lbHNlKHZhcmlhYmxlID09ICIuVFJVRSIsICJSZWFkbWl0dGVkIiwgdmFyaWFibGUpKSAlPiUgDQogICAgbGVmdF9qb2luKC4sIGljZHMgJT4lIHJlbmFtZShjb2RlID0gImljZCIpLCBieSA9ICJjb2RlIikgJT4lIA0KICAgIHVuZ3JvdXAoKSAlPiUgDQogICAgZmlsdGVyKCFjb25jZXB0X3R5cGUgPT0gImljZC05IikNCg0KDQogICMgcmVmYWN0b3IgbmV1cm8gc3RhdHVzDQogIGRpYWdfdGFibGVfd2lkZV9hbGwkcG5zX2NucyA8LSBmYWN0b3IoZGlhZ190YWJsZV93aWRlX2FsbCRwbnNfY25zLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscz1jKCJDZW50cmFsIiwgIlBlcmlwaGVyYWwiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiQ05TIiwgIlBOUyIpKQ0KICANCiAgDQogIHJldHVybihkaWFnX3RhYmxlX3dpZGVfYWxsKQ0KICANCiAgfQ0KYGBgDQoNCmBgYHtyfQ0Kc3RyYXRpZmllZF9uZXVyb19jb3VudHNfYWR1bHQgPC0gY29tcHV0ZV9zdHJhdGlmaWVkX25ldXJvX2NvdW50cyhzb3J0ZWRfc2l0ZXMgPSBhZHVsdF9zaXRlcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaXNfcGVkaWF0cmljID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRhYmxlb25lID0gdGFibGVvbmVfYWR1bHQpDQoNCnN0cmF0aWZpZWRfbmV1cm9fY291bnRzX3BlZGlhdHJpYyA8LSBjb21wdXRlX3N0cmF0aWZpZWRfbmV1cm9fY291bnRzKHNvcnRlZF9zaXRlcyA9IHBlZGlhdHJpY19zaXRlcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaXNfcGVkaWF0cmljID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGFibGVvbmUgPSB0YWJsZW9uZV9wZWRpYXRyaWMpDQpgYGANCg0KDQpgYGB7cn0NCmNvbXB1dGVfc3RyYXRpZmllZF9uZXVyb19jb3VudHNfYWdlIDwtIGZ1bmN0aW9uKHN0cmF0aWZpZWRfbmV1cm9fY291bnRzLCB0YWJsZW9uZSkgew0KICANCiAgc3RyYXRpZmllZF9uZXVyb19jb3VudHMkdmFyaWFibGUgPC0gZmFjdG9yKHN0cmF0aWZpZWRfbmV1cm9fY291bnRzJHZhcmlhYmxlLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHM9YygiLjAwdG8wMiIsICIuMDN0bzA1IiwgIi4wNnRvMTEiLCAiLjEydG8xNyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIi4xOHRvMjUiLCAiLjI2dG80OSIsICIuNTB0bzY5IiwgIi43MHRvNzkiLCAiLjgwcGx1cyIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiMC0yIFllYXJzIiwgIjMtNSBZZWFycyIsICI2LTExIFllYXJzIiwgIjEyLTE3IFllYXJzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMTgtMjUgWWVhcnMiLCAiMjYtNDkgWWVhcnMiLCAiNTAtNjkgWWVhcnMiLCAiNzAtNzkgWWVhcnMiLCAiODArIFllYXJzIikpDQogIA0KICAjIHRvdGFsIGNvdW50cw0KICBuXzBfMiA8LSBnZXRfdGFibGVfbih0YWJsZW9uZSwgdmFyID0gIjAtMiIpDQogIG5fM181ID0gZ2V0X3RhYmxlX24odGFibGVvbmUsIHZhciA9ICIzLTUiKQ0KICBuXzZfMTEgPSBnZXRfdGFibGVfbih0YWJsZW9uZSwgdmFyID0gIjYtMTEiKQ0KICBuXzEyXzE3ID0gZ2V0X3RhYmxlX24odGFibGVvbmUsIHZhciA9ICIxMi0xNyIpDQogIG5fMThfMjUgPSBnZXRfdGFibGVfbih0YWJsZW9uZSwgdmFyID0gIjE4LTI1IikNCiAgbl8yNl80OSA9IGdldF90YWJsZV9uKHRhYmxlb25lLCB2YXIgPSAiMjYtNDkiKQ0KICBuXzUwXzY5ID0gZ2V0X3RhYmxlX24odGFibGVvbmUsIHZhciA9ICI1MC02OSIpDQogIG5fNzBfNzkgPSBnZXRfdGFibGVfbih0YWJsZW9uZSwgdmFyID0gIjcwLTc5IikNCiAgbl84MCA9IGdldF90YWJsZV9uKHRhYmxlb25lLCB2YXIgPSAiODArIikNCiAgDQogIGFnZV9kZiA8LSBkYXRhLmZyYW1lKCJ2YXJpYWJsZSIgPSBjKCIwLTIgWWVhcnMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjMtNSBZZWFycyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjYtMTEgWWVhcnMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIxMi0xNyBZZWFycyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMTgtMjUgWWVhcnMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIyNi00OSBZZWFycyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjUwLTY5IFllYXJzIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiNzAtNzkgWWVhcnMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI4MCsgWWVhcnMiKSwNCiAgICAgICAgICAgICAgICAgICAgICJuX3ZhciIgPSBjKG5fMF8yLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5fM181LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5fNl8xMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuXzEyXzE3LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5fMThfMjUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbl8yNl80OSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuXzUwXzY5LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5fNzBfNzksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbl84MCkpDQoNCiAgDQogIHN0cmF0aWZpZWRfbmV1cm9fY291bnRzIDwtIHN0cmF0aWZpZWRfbmV1cm9fY291bnRzICU+JSANCiAgICBmaWx0ZXIoZ3JlcGwoIlllYXJzIiwgdmFyaWFibGUpLA0KICAgICAgICAgICAgICAgICFjb2RlID09ICJOTiIsIA0KICAgICAgICAgICAgICAgICF0b3RhbF9uX3ZhciA9PSAwKSAlPiUgDQogICAgICAgICAjIHRvdGFsX24gd291bGQgYmUgY2FsY3VsYXRpbmcgdG90YWwgY29kZXMgYWNyb3NzIGVhY2ggYWdlIGdyb3VwIHdoaWNoIGlzIG5vdCB0cnVlIHJlZmxlY3Rpb24gb2YgcGF0aWVudCBjb3VudHMgYi9jIHBhdGllbnRzIGNhbiBoYXZlIG1vcmUgdGhhbiBvbmUgY29kZQ0KICAgICAgICAgI211dGF0ZSh0b3RhbF9uID0gc3VtKHRvdGFsX25fdmFyKSkgJT4lIA0KICAgICAgICAgdW5ncm91cCgpICU+JSANCiAgICAgICAgIGxlZnRfam9pbiguLCBhZ2VfZGYsIGJ5ID0gInZhcmlhYmxlIikgJT4lIA0KICAgICAgICAgbXV0YXRlKG5fdmFyID0gYXMubnVtZXJpYyhuX3ZhciksDQogICAgICAgICAgICAgICAgcGVyYyA9IGFzLmNoYXJhY3Rlcihyb3VuZCh0b3RhbF9uX3Zhci9uX3ZhcioxMDAsMSkpLA0KICAgICAgICAgICAgICAgIHBlcmMgPSBpZl9lbHNlKHBlcmMgPCAwLjEsICc8MC4xJywgcGVyYyksDQogICAgICAgICAgICAgICAgcGVyYyA9IHBhc3RlMChwZXJjLCAiJSIpKSANCiAgDQogIHJldHVybihzdHJhdGlmaWVkX25ldXJvX2NvdW50cykNCiAgDQogIA0KfQ0KDQoNCmBgYA0KDQpgYGB7cn0NCnN0cmF0aWZpZWRfbmV1cm9fY291bnRzX2FnZV9hZHVsdCA8LSBjb21wdXRlX3N0cmF0aWZpZWRfbmV1cm9fY291bnRzX2FnZShzdHJhdGlmaWVkX25ldXJvX2NvdW50cyA9IHN0cmF0aWZpZWRfbmV1cm9fY291bnRzX2FkdWx0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRhYmxlb25lID0gdGFibGVvbmVfYWR1bHQpDQoNCnN0cmF0aWZpZWRfbmV1cm9fY291bnRzX2FnZV9wZWRpYXRyaWMgPC0gY29tcHV0ZV9zdHJhdGlmaWVkX25ldXJvX2NvdW50c19hZ2Uoc3RyYXRpZmllZF9uZXVyb19jb3VudHMgPSBzdHJhdGlmaWVkX25ldXJvX2NvdW50c19wZWRpYXRyaWMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRhYmxlb25lID0gdGFibGVvbmVfcGVkaWF0cmljKQ0KYGBgDQoNCioqRGlhZ25vc2VzIGJ5IEFkdWx0IGFuZCBQZWRpYXRyaWMgUG9wdWxhdGlvbnMqKg0KDQpgYGB7ciBmaWcuaGVpZ2h0PTIwLCBmaWcud2lkdGg9MTV9DQpuXzBfMiA8LSBnZXRfdGFibGVfbih0YWJsZW9uZV9jb21iaW5lLCB2YXIgPSAiMC0yIikNCiNuXzNfNSA8LSBnZXRfdGFibGVfbih0YWJsZW9uZV9jb21iaW5lLCB2YXIgPSAiMy01IikNCm5fNl8xMSA8LSBnZXRfdGFibGVfbih0YWJsZW9uZV9jb21iaW5lLCB2YXIgPSAiNi0xMSIpDQpuXzEyXzE3IDwtIGdldF90YWJsZV9uKHRhYmxlb25lX2NvbWJpbmUsIHZhciA9ICIxMi0xNyIpDQoNCnRvdGFsX3BlZF9hZ2VfY291bnQgPSBhcy5udW1lcmljKG5fMF8yKSArICBhcy5udW1lcmljKG5fNl8xMSkgKyBhcy5udW1lcmljKG5fMTJfMTcpDQoNCnN0cmF0aWZpZWRfbmV1cm9fY291bnRzX2FnZV9wZWRpYXRyaWNfcmVmb3JtYXQgPSBzdHJhdGlmaWVkX25ldXJvX2NvdW50c19hZ2VfcGVkaWF0cmljICU+JQ0KICBzZWxlY3QodmFyaWFibGUsIHBuc19jbnMsIGRlc2NyaXB0aW9uLCBuX3ZhciwgdG90YWxfbl92YXIpICU+JQ0KICBtdXRhdGUodmFyaWFibGUgPSAiMC0xNyBZZWFycyIpICU+JQ0KICBncm91cF9ieShwbnNfY25zLCBkZXNjcmlwdGlvbikgJT4lDQogIG11dGF0ZSh0b3RhbF9uX3ZhciA9IHN1bSh0b3RhbF9uX3ZhciwgbmEucm0gPSBUUlVFKSkgJT4lDQogIHVuZ3JvdXAoKSAlPiUNCiAgIyBuX3ZhciBpcyB0aGUgdG90YWwgcGVyIGBhZ2VfZ3JvdXBgIHN0cmF0aWZpY2F0aW9uDQogIHNlbGVjdCgtbl92YXIpICU+JQ0KICBkaXN0aW5jdCgpICU+JQ0KICBtdXRhdGUocGVyYyA9IHJvdW5kKHRvdGFsX25fdmFyL3RvdGFsX3BlZF9hZ2VfY291bnQqMTAwLDEpLA0KICAgICAgICAgcGVyYyA9IGlmX2Vsc2UocGVyYyA8IDAuMSwgJzwwLjEnLCBhcy5jaGFyYWN0ZXIocGVyYykpLA0KICAgICAgICAgcGVyYyA9IHBhc3RlMChwZXJjLCAiJSIpKQ0KDQpzdHJhdGlmaWVkX25ldXJvX2NvdW50c19hZ2VfY29tYmluZWQgPC0gc3RyYXRpZmllZF9uZXVyb19jb3VudHNfYWdlX2FkdWx0ICU+JQ0KICBzZWxlY3QodmFyaWFibGUsIHBuc19jbnMsIGRlc2NyaXB0aW9uLCB0b3RhbF9uX3ZhciwgcGVyYykgJT4lDQogIHJiaW5kKC4sIHN0cmF0aWZpZWRfbmV1cm9fY291bnRzX2FnZV9wZWRpYXRyaWNfcmVmb3JtYXQgJT4lDQogICAgICAgICAgc2VsZWN0KHZhcmlhYmxlLCBwbnNfY25zLCBkZXNjcmlwdGlvbiwgdG90YWxfbl92YXIsIHBlcmMpKQ0KDQpzdHJhdGlmaWVkX25ldXJvX2NvdW50c19hZ2VfY29tYmluZWQkdmFyaWFibGUgPC0gZmFjdG9yKHN0cmF0aWZpZWRfbmV1cm9fY291bnRzX2FnZV9jb21iaW5lZCR2YXJpYWJsZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHM9YygiMC0xNyBZZWFycyIsICIxOC0yNSBZZWFycyIsICIyNi00OSBZZWFycyIsICI1MC02OSBZZWFycyIsICI3MC03OSBZZWFycyIsICI4MCsgWWVhcnMiKSkNCmBgYA0KDQpgYGB7ciBmaWcuaGVpZ2h0PTIwLCBmaWcud2lkdGg9MTJ9DQpncm91cC5jb2xvcnMgPC0gYyhOTkMgPSAiZ3JheSIsIFBOUyA9ICJzbGF0ZWJsdWUiLCBDTlMgPSJ0b21hdG8iKQ0KDQphZHVsdF9wZWRfY29tYmluZWRfcGxvdCA8LSBnZ3Bsb3Qoc3RyYXRpZmllZF9uZXVyb19jb3VudHNfYWdlX2NvbWJpbmVkLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSB0b3RhbF9uX3ZhciwgeSA9IHJlb3JkZXIoZGVzY3JpcHRpb24sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b3RhbF9uX3ZhciksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBwbnNfY25zKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKw0KICBnZW9tX3RleHQoDQogICAgYWVzKHggPSB0b3RhbF9uX3ZhciwgeSA9IGRlc2NyaXB0aW9uLCBsYWJlbCA9IHBlcmMpLA0KICAgIGhqdXN0ID0gLTAuMSwNCiAgICAjaGp1c3Q9Imlud2FyZCIsDQogICAgc2l6ZSA9IDYsDQogICAgaW5oZXJpdC5hZXMgPSBUUlVFKSArDQogIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IGMoMCwwLjIpKSkgKyAjIHRvIGVuc3VyZSBwZXJjZW50IGxhYmVscyBhcmUgd2l0aGluIHRoZSBmaWd1cmUNCiAgZmFjZXRfZ3JpZChwbnNfY25zIH4gdmFyaWFibGUsIHNjYWxlcyA9ICJmcmVlIiwgc3BhY2UgPSAiZnJlZV95IikgKyANCiAgc2NhbGVfeV9kaXNjcmV0ZSgiIiwgcG9zaXRpb249ImxlZnQiLCBsYWJlbHMgPSBmdW5jdGlvbih4KSBzdHJfd3JhcCh4LCB3aWR0aCA9IDEwMCkpICsgICANCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gZ3JvdXAuY29sb3JzKSArDQogIHhsYWIoIk51bWJlciBvZiBQYXRpZW50cyIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLA0KICAgICAgICBzdHJpcC50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDQwKSwNCiAgICAgICAgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzNSksDQogICAgICAgIGF4aXMudGV4dC55LmxlZnQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDI1LCBmYWNlID0gImJvbGQiKSwNCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDI1KSwNCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzNSkpDQoNCmdnc2F2ZSgiZmlndXJlcy9GaWczX2RpYWdub3Nlc19ieV9hZ2VfYWR1bHRfcGVkXzE4Z3JvdXAucG5nIiwgYWR1bHRfcGVkX2NvbWJpbmVkX3Bsb3QsIHdpZHRoID0gNDAsIGhlaWdodCA9IDE5LCBkcGkgPSAzMDApDQpgYGANCg0KIVtdKGZpZ3VyZXMvRmlnM19kaWFnbm9zZXNfYnlfYWdlX2FkdWx0X3BlZF8xOGdyb3VwLnBuZyl7I2lkIC5jbGFzcyB3aWR0aD0xMDAwIGhlaWdodD04MDBweH0NCg0KIyMgRXZhbHVhdGUgRGlhZ25vc2VzIGJ5IE91dGNvbWUNCg0KYGBge3J9DQoNCnN0cmF0aWZpZWRfbmV1cm9fY291bnRzX2FkdWx0JHBvcHVsYXRpb24gPSAiYWR1bHQiDQpzdHJhdGlmaWVkX25ldXJvX2NvdW50c19wZWRpYXRyaWMkcG9wdWxhdGlvbiA9ICJwZWRpYXRyaWMiDQoNCnN0cmF0aWZpZWRfbmV1cm9fY291bnRzX2NvbWJpbmUgPC0gcmJpbmQoc3RyYXRpZmllZF9uZXVyb19jb3VudHNfYWR1bHQsIHN0cmF0aWZpZWRfbmV1cm9fY291bnRzX3BlZGlhdHJpYykNCg0KIyMgZGV0ZXJtaW5lIHRvdGFsIG51bWJlciBvZiBwYXRpZW50cyB3aG8gZGllZCBvciBzZXZlcmUNCm5fZGVhdGhfYWR1bHQgPSBnZXRfdGFibGVfbihkZiA9IHRhYmxlb25lX2FkdWx0LCB2YXIgPSAiRGVjZWFzZWQiKSAlPiUgYXMubnVtZXJpYygpDQpuX3NldmVyZV9hZHVsdCA9IGdldF90YWJsZV9uKGRmID0gdGFibGVvbmVfYWR1bHQsIHZhciA9ICJTZXZlcmUiKSAlPiUgYXMubnVtZXJpYygpDQoNCm5fZGVhdGhfcGVkaWF0cmljID0gZ2V0X3RhYmxlX24oZGYgPSB0YWJsZW9uZV9wZWRpYXRyaWMsIHZhciA9ICJEZWNlYXNlZCIpICU+JSBhcy5udW1lcmljKCkNCm5fc2V2ZXJlX3BlZGlhdHJpYyA9IGdldF90YWJsZV9uKGRmID0gdGFibGVvbmVfcGVkaWF0cmljLCB2YXIgPSAiU2V2ZXJlIikgJT4lIGFzLm51bWVyaWMoKQ0KDQpzdHJhdGlmaWVkX25ldXJvX2NvdW50c19vdXRjb21lcyA8LSBzdHJhdGlmaWVkX25ldXJvX2NvdW50c19jb21iaW5lICU+JSANCiAgZmlsdGVyKHZhcmlhYmxlICVpbiUgYygiU2V2ZXJlIiwgIkRlY2Vhc2VkIiksDQogICAgICAgICAhY29kZSA9PSAiTk4iLCANCiAgICAgICAgICF0b3RhbF9uX3ZhciA9PSAwKSAlPiUgICAgICAgICAgDQogICAgICAgICAjIHRvdGFsX24gd291bGQgYmUgY2FsY3VsYXRpbmcgdG90YWwgY29kZXMgYWNyb3NzIGVhY2ggb3V0Y29tZSBncm91cCB3aGljaCBpcyBub3QgdHJ1ZSByZWZsZWN0aW9uIG9mIHBhdGllbnQgY291bnRzIGIvYyBwYXRpZW50cyBjYW4gaGF2ZSBtb3JlIHRoYW4gb25lIGNvZGUNCiAgICAgICAgICNtdXRhdGUodG90YWxfbiA9IHN1bSh0b3RhbF9uX3ZhcikpICU+JSANCiAgICAgICAgIGdyb3VwX2J5KHZhcmlhYmxlLCBkZXNjcmlwdGlvbiwgcG9wdWxhdGlvbikgJT4lIA0KICBtdXRhdGUodG90YWxfbl92YXIgPSBzdW0odG90YWxfbl92YXIpKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIG11dGF0ZShwZXJjID0gY2FzZV93aGVuKHZhcmlhYmxlID09ICJTZXZlcmUiICYgcG9wdWxhdGlvbiA9PSAiYWR1bHQiIH4gcm91bmQodG90YWxfbl92YXIvbl9zZXZlcmVfYWR1bHQqMTAwLDEpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUgPT0gIkRlY2Vhc2VkIiAmIHBvcHVsYXRpb24gPT0gImFkdWx0IiB+IHJvdW5kKHRvdGFsX25fdmFyL25fZGVhdGhfYWR1bHQqMTAwLDEpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUgPT0gIlNldmVyZSIgJiBwb3B1bGF0aW9uID09ICJwZWRpYXRyaWMiIH4gcm91bmQodG90YWxfbl92YXIvbl9zZXZlcmVfcGVkaWF0cmljKjEwMCwxKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlID09ICJEZWNlYXNlZCIgJiBwb3B1bGF0aW9uID09ICJwZWRpYXRyaWMiIH4gcm91bmQodG90YWxfbl92YXIvbl9kZWF0aF9wZWRpYXRyaWMqMTAwLDEpKSwNCiAgICAgICAgIHBlcmMgPSBpZl9lbHNlKHBlcmMgPCAwLjEsICc8MC4xJywgYXMuY2hhcmFjdGVyKHBlcmMpKSwNCiAgICAgICBwZXJjID0gcGFzdGUwKHBlcmMsICIlIikpICU+JSANCiAgc2VsZWN0KC1jb2RlLCAtY29uY2VwdF90eXBlKSAlPiUgDQogIGRpc3RpbmN0KCkNCmBgYA0KDQpgYGB7cn0NCmFkdWx0X291dGNvbWVzID0gZ2dwbG90KHN0cmF0aWZpZWRfbmV1cm9fY291bnRzX291dGNvbWVzICU+JSANCiAgICAgICAgIGZpbHRlcihwb3B1bGF0aW9uID09ICJhZHVsdCIpICU+JSANCiAgICAgICAgICAgbXV0YXRlKHZhcmlhYmxlID0gYXMuZmFjdG9yKHZhcmlhYmxlKSAlPiUgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmY3RfcmVjb2RlKA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBNb3J0YWxpdHkgPSAiRGVjZWFzZWQiKSksDQogICAgICAgYWVzKHggPSB0b3RhbF9uX3ZhciwgeSA9IHJlb3JkZXIoZGVzY3JpcHRpb24sIHRvdGFsX25fdmFyKSwgZmlsbCA9IHBuc19jbnMpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArDQogZ2VvbV90ZXh0KA0KICAgIGFlcyh4ID0gdG90YWxfbl92YXIsIHkgPSBkZXNjcmlwdGlvbiwgbGFiZWwgPSBwZXJjKSwNCiAgICBoanVzdCA9IC0wLjEsDQogICAgI2hqdXN0PSJpbndhcmQiLA0KICAgIHNpemUgPSA1LA0KICAgIGluaGVyaXQuYWVzID0gVFJVRSkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKDAsMC4yKSkpICsgDQogIGZhY2V0X2dyaWQocG5zX2NucyB+IHZhcmlhYmxlLCBzY2FsZXMgPSAiZnJlZSIsIHNwYWNlID0gImZyZWVfeSIpICsgDQogIHNjYWxlX3lfZGlzY3JldGUoIiIsIHBvc2l0aW9uPSJsZWZ0IiwgbGFiZWxzID0gZnVuY3Rpb24oeCkgc3RyX3dyYXAoeCwgd2lkdGggPSAxMDApKSArICAgDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGdyb3VwLmNvbG9ycykgKw0KICB4bGFiKCJOdW1iZXIgb2YgUGF0aWVudHMiKSArIA0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwNCiAgICAgICAgc3RyaXAudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzNSksDQogICAgICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMzApLA0KICAgICAgICBheGlzLnRleHQueS5sZWZ0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCwgZmFjZSA9ICJib2xkIiksDQogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSksDQogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMzUpKQ0KDQpnZ3NhdmUoImZpZ3VyZXMvU3VwcEZpZzFfZGlhZ25vc2VzX2J5X291dGNvbWVzX2FkdWx0LnBuZyIsIGFkdWx0X291dGNvbWVzLCB3aWR0aCA9IDI1LCBoZWlnaHQgPSAxNSwgZHBpID0gMzAwKQ0KYGBgDQoNCmBgYHtyfQ0KcGVkaWF0cmljX291dGNvbWVzID0gZ2dwbG90KHN0cmF0aWZpZWRfbmV1cm9fY291bnRzX291dGNvbWVzICU+JSANCiAgICAgICAgIGZpbHRlcihwb3B1bGF0aW9uID09ICJwZWRpYXRyaWMiKSAlPiUgDQogICAgICAgICAgIG11dGF0ZSh2YXJpYWJsZSA9IGFzLmZhY3Rvcih2YXJpYWJsZSkgJT4lIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmN0X3JlY29kZSgNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTW9ydGFsaXR5ID0gIkRlY2Vhc2VkIikpLA0KICAgICAgIGFlcyh4ID0gdG90YWxfbl92YXIsIHkgPSByZW9yZGVyKGRlc2NyaXB0aW9uLCB0b3RhbF9uX3ZhciksIGZpbGwgPSBwbnNfY25zKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKw0KIGdlb21fdGV4dCgNCiAgICBhZXMoeCA9IHRvdGFsX25fdmFyLCB5ID0gZGVzY3JpcHRpb24sIGxhYmVsID0gcGVyYyksDQogICAgaGp1c3QgPSAtMC4xLA0KICAgICNoanVzdD0iaW53YXJkIiwNCiAgICBzaXplID0gNSwNCiAgICBpbmhlcml0LmFlcyA9IFRSVUUpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLDAuMikpKSArIA0KICBmYWNldF9ncmlkKHBuc19jbnMgfiB2YXJpYWJsZSwgc2NhbGVzID0gImZyZWUiKSArIA0KICBzY2FsZV95X2Rpc2NyZXRlKCIiLCBwb3NpdGlvbj0ibGVmdCIsIGxhYmVscyA9IGZ1bmN0aW9uKHgpIHN0cl93cmFwKHgsIHdpZHRoID0gMTAwKSkgKyAgIA0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBncm91cC5jb2xvcnMpICsNCiAgeGxhYigiIikgKyANCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLA0KICAgICAgICBzdHJpcC50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDM1KSwNCiAgICAgICAgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzMCksDQogICAgICAgIGF4aXMudGV4dC55LmxlZnQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwLCBmYWNlID0gImJvbGQiKSwNCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSwNCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzNSkpDQoNCmdnc2F2ZSgiZmlndXJlcy9TdXBwRmlnMV9kaWFnbm9zZXNfYnlfb3V0Y29tZXNfcGVkaWF0cmljLnBuZyIsIHBlZGlhdHJpY19vdXRjb21lcywgd2lkdGggPSAyNSwgaGVpZ2h0ID0gOSwgZHBpID0gMzAwKQ0KYGBgDQoNCg0KYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1UUlVFfQ0KI2dyaWQuYXJyYW5nZShwZWRpYXRyaWNfb3V0Y29tZXMsIGFkdWx0X291dGNvbWVzLCBuY29sPTEpDQoNCmdnc2F2ZSgiZmlndXJlcy9TdXBwRmlnMV9kaWFnbm9zZXNfYnlfb3V0Y29tZV9jb21iaW5lZC5wbmciLCBncmlkLmFycmFuZ2UocGVkaWF0cmljX291dGNvbWVzLCBhZHVsdF9vdXRjb21lcywgbmNvbD0xKSwgd2lkdGggPSAyNSwgaGVpZ2h0ID0gMjUsIGRwaSA9IDMwMCkNCmBgYA0KDQohW10oZmlndXJlcy9TdXBwRmlnMV9kaWFnbm9zZXNfYnlfb3V0Y29tZV9jb21iaW5lZC5wbmcpeyNpZCAuY2xhc3Mgd2lkdGg9ODAwIGhlaWdodD04MDBweH0NCg0KIyMgRXZhbHVhdGUgcGF0aWVudHMgd2l0aCAiQm90aCIgQ05TIGFuZCBQTlMgZGlzZWFzZXMNCg0KVGhlc2UgcGF0aWVudHMgd2VyZSBleGNsdWRlZCBmcm9tIHRoZSBwcmltYXJ5IGFuYWx5c2lzIGR1ZSB0byBwb3RlbnRpYWwgY29uZm91bmRpbmcuDQoNCmBgYHtyfQ0KIyBjcmVhdGUgZW1wdHkgbGlzdCB0byBzdG9yZSB0aGUgY291bnRzIG9mICJib3RoIiBwYXRpZW50cw0KYm90aF9jb3VudHNfbGlzdCA8LSBsaXN0KCkNCg0KIyBmb3IgZWFjaCBoZWFsdGhjYXJlIHN5c3RlbSwgcmV0cmlldmUgdGhlIG51bWJlciBvZiBleGNsdWRlZCAiYm90aCIgcGF0aWVudHMgcmVjb3JkZWQNCmZvciAoaSBpbiBzb3J0ZWRfc2l0ZXMpIHsNCiAgbl9ib3RoIDwtIHJlc3VsdHNbW3Bhc3RlKGkpXV1bWyJmaXJzdF9ob3NwX3Jlc3VsdHMiXV1bWyJib3RoX2NvdW50cyJdXQ0KICANCiAgYm90aF9jb3VudHNfbGlzdFtbaV1dIDwtIG5fYm90aA0KfQ0KDQpib3RoX2NvdW50cyA8LSBiaW5kX3Jvd3MoYm90aF9jb3VudHNfbGlzdCkgJT4lIHQoKSAlPiUgZGF0YS5mcmFtZSgpICU+JSBzdW0oKQ0KDQpwcmludChwYXN0ZShib3RoX2NvdW50cywgIigiLCByb3VuZChib3RoX2NvdW50cy8oYXMubnVtZXJpYyh0YWJsZW9uZV9jb21iaW5lJE5bMV0pK2JvdGhfY291bnRzKSoxMDAsMiksICIlKSIpKSANCmBgYA0KDQojICoqQ29tb3JiaWRpdHkgQW5hbHlzaXMqKg0KDQpDcmVhdGUgY29tb3JiaWRpdHkgY291bnQgdGFibGUNCg0KYGBge3J9DQpjb21vcmJpZGl0eV90YWJsZV9hZHVsdCA8LSBjb21vcmJfdGFibGVfd2lkZSAlPiUNCiAgc2VsZWN0KENvbW9yYmlkaXR5LCBwb3B1bGF0aW9uLCBDb21vcmJfVG90YWwsIGBOTkNfTl8lYCwgYENOU19OXyVgLCBgUE5TX05fJWApICU+JQ0KICBmaWx0ZXIocG9wdWxhdGlvbiA9PSAiQWR1bHQiKSAlPiUgDQogIHJlbmFtZShOID0gIkNvbW9yYl9Ub3RhbCIsDQogICAgICAgICBOTkMgPSAiTk5DX05fJSIsDQogICAgICAgICBDTlMgPSAiQ05TX05fJSIsDQogICAgICAgICBQTlMgPSAiUE5TX05fJSIpICU+JSANCiAgbXV0YXRlKE4gPSBwYXN0ZTAoTiwgIiAoIiwgcm91bmQoTi9uX2FkdWx0LCAyKSoxMDAsICIlKSIpKQ0KDQpjb21vcmJpZGl0eV90YWJsZV9wZWRpYXRyaWMgPC0gY29tb3JiX3RhYmxlX3dpZGUgJT4lDQogIHNlbGVjdChDb21vcmJpZGl0eSwgcG9wdWxhdGlvbiwgQ29tb3JiX1RvdGFsLCBgTk5DX05fJWAsIGBDTlNfTl8lYCwgYFBOU19OXyVgKSAlPiUNCiAgZmlsdGVyKHBvcHVsYXRpb24gPT0gIlBlZGlhdHJpYyIpICU+JSANCiAgcmVuYW1lKE4gPSAiQ29tb3JiX1RvdGFsIiwNCiAgICAgICAgIE5OQyA9ICJOTkNfTl8lIiwNCiAgICAgICAgIENOUyA9ICJDTlNfTl8lIiwNCiAgICAgICAgIFBOUyA9ICJQTlNfTl8lIikgJT4lIA0KICBtdXRhdGUoTiA9IHBhc3RlMChOLCAiICgiLCByb3VuZChOL25fcGVkaWF0cmljLCAyKSoxMDAsICIlKSIpKQ0KDQpzdXBwX2NvbW9yYmlkaXR5X3RhYmxlIDwtIHJiaW5kKGNvbW9yYmlkaXR5X3RhYmxlX2FkdWx0LCBjb21vcmJpZGl0eV90YWJsZV9wZWRpYXRyaWMpICU+JSANCiAgYXJyYW5nZShDb21vcmJpZGl0eSkNCg0Kd3JpdGUuY3N2KHN1cHBfY29tb3JiaWRpdHlfdGFibGUsICJ0YWJsZXMvU3VwcFRhYmxlX0NvbW9yYmlkaXR5X0NvdW50cy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkNCmBgYA0KDQpDcmVhdGUgdmVjdG9yIGNvdW50cyBvZiBwYXRpZW50cyB3aXRoIGNvbW9yYmlkaXR5IGRhdGEgDQoNCmBgYHtyfQ0KbmV1cm9fcHRfY291bnRzX3N1bV9hZHVsdCA8LSBuZXVyb19wdF9jb3VudHMgJT4lIA0KICBmaWx0ZXIocG9wdWxhdGlvbiA9PSAiQWR1bHQiKSAlPiUgDQogIHNlbGVjdCgtcG9wdWxhdGlvbikNCg0KbmV1cm9fcHRfY291bnRzX3N1bV9wZWRpYXRyaWMgPC0gbmV1cm9fcHRfY291bnRzICU+JSANCiAgZmlsdGVyKHBvcHVsYXRpb24gPT0gIlBlZGlhdHJpYyIpICU+JSANCiAgc2VsZWN0KC1wb3B1bGF0aW9uKQ0KDQpub25lX25fYWR1bHQgPSBuZXVyb19wdF9jb3VudHNfc3VtX2FkdWx0JE5vbmVfbg0KY25zX25fYWR1bHQgPSBuZXVyb19wdF9jb3VudHNfc3VtX2FkdWx0JENOU19uDQpwbnNfbl9hZHVsdCA9IG5ldXJvX3B0X2NvdW50c19zdW1fYWR1bHQkUE5TX24NCg0Kbm9uZV9uX3BlZGlhdHJpYyA9IG5ldXJvX3B0X2NvdW50c19zdW1fcGVkaWF0cmljJE5vbmVfbg0KY25zX25fcGVkaWF0cmljID0gbmV1cm9fcHRfY291bnRzX3N1bV9wZWRpYXRyaWMkQ05TX24NCnBuc19uX3BlZGlhdHJpYyA9IG5ldXJvX3B0X2NvdW50c19zdW1fcGVkaWF0cmljJFBOU19uDQpgYGANCg0KIyMjIENhbGN1bGF0ZSBSZWxhdGl2ZSBSaXNrIChSUikNCg0KYGBge3J9DQpjYWxjdWxhdGVfcnIgPC0gZnVuY3Rpb24oaXNfcGVkaWF0cmljID0gRkFMU0UpIHsNCiAgDQogIGlmKGlzX3BlZGlhdHJpYyA9PSBGQUxTRSkgew0KICAgIGNuc19uID0gY25zX25fYWR1bHQNCiAgICBwbnNfbiA9IHBuc19uX2FkdWx0DQogICAgbmV1cm9fcHRfY291bnRzX3N1bSA9IG5ldXJvX3B0X2NvdW50c19zdW1fYWR1bHQNCiAgICBwb3AgPSAiQWR1bHQiDQogICAgIyBjcmVhdGUgYSBsaXN0IG9mIGNvbW9yYmlkaXRpZXMNCiAgICBsaXN0X29mX2NvbW9yYnMgPC0gY29tb3JiX3RhYmxlX3dpZGUgJT4lIA0KICAgICAgZmlsdGVyKHBvcHVsYXRpb24gPT0gcGFzdGUocG9wKSkgJT4lIA0KICAgICAgZGlzdGluY3QoQ29tb3JiaWRpdHkpIA0KICAgIGxpc3Rfb2ZfY29tb3JicyA8LSBsaXN0X29mX2NvbW9yYnMkQ29tb3JiaWRpdHkNCiAgICB9IGVsc2UgIHsNCiAgICBjbnNfbiA9IGNuc19uX3BlZGlhdHJpYw0KICAgIHBuc19uID0gcG5zX25fcGVkaWF0cmljDQogICAgbmV1cm9fcHRfY291bnRzX3N1bSA9IG5ldXJvX3B0X2NvdW50c19zdW1fcGVkaWF0cmljDQogICAgcG9wID0gIlBlZGlhdHJpYyINCiAgICAjIGNyZWF0ZSBhIGxpc3Qgb2YgY29tb3JiaWRpdGllcw0KICAgIGxpc3Rfb2ZfY29tb3JicyA8LSBjb21vcmJfdGFibGVfd2lkZSAlPiUgDQogICAgICBmaWx0ZXIocG9wdWxhdGlvbiA9PSBwYXN0ZShwb3ApKSAlPiUgDQogICAgICBkaXN0aW5jdChDb21vcmJpZGl0eSkgJT4lIA0KICAgICAgYXMudmVjdG9yKCkNCiAgICBsaXN0X29mX2NvbW9yYnMgPC0gbGlzdF9vZl9jb21vcmJzJENvbW9yYmlkaXR5DQogIH0NCiAgDQogIA0KICAjIGNyZWF0ZSBlbXB0eSBsaXN0IHRvIHNhdmUgcmVsYXRpdmUgcmlzayBjYWxjdWxhdGlvbnMNCiAgcnJfY2FsY3MgPC0gbGlzdCgpDQogIA0KICAjIGZvciBlYWNoIGNvbW9yYmlkaXR5LCB3ZSB3aWxsIGNhbGN1bGF0ZSB0aGUgcmVsYXRpdmUgcmlzayBvZiBoYXZpbmcgYSBDTlMgYW5kIFBOUyBkaWFnbm9zaXMNCiAgZm9yIChpIGluIGxpc3Rfb2ZfY29tb3Jicykgew0KICANCiAgICAjIGh0dHBzOi8vd3d3LmNkYy5nb3YvY3NlbHMvZHNlcGQvc3MxOTc4L2xlc3NvbjMvc2VjdGlvbjUuaHRtbA0KICAgICMgY3JlYXRlIG1hdHJpeCBhczoNCiAgICAjIGV4cG9zZWQgZ3JvdXAgLSB3aXRoIG91dGNvbWUsIHdpdGhvdXQgb3V0Y29tZQ0KICAgICMgbm9uIGV4cG9zZWQgZ3JvdXAgLSB3aXRoIG91dGNvbWUsIHdpdGhvdXQgb3V0Y29tZQ0KICANCiAgICAjIHdoZXJlIHZhcmlhYmxlcyBlbmRpbmcgd2l0aCBgX1RvdGFsYCBhcmUgdGhlIHRvdGFsIG51bWJlciBvZiBDTlMsIFBOUywgb3IgTk5DIHBhdGllbnRzIHdpdGggdGhlIHJlc3BlY3RpdmUgY29tb3JiaWRpdHkuDQogICAgIyB2YXJpYWJsZXMgZW5kaW5nIHdpdGggYF9uYCBhcmUgdGhlIHRvdGFsIG51bWJlcnMgaW4gdGhlIGVudGlyZSBwb3B1bGF0aW9uDQogIA0KICAgICMgdG8gY2FsY3VsYXRlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzDQogICNodHRwczovL3NwaHdlYi5idW1jLmJ1LmVkdS9vdGx0L21waC1tb2R1bGVzL2JzL2JzNzA0X2NvbmZpZGVuY2VfaW50ZXJ2YWxzL2JzNzA0X2NvbmZpZGVuY2VfaW50ZXJ2YWxzOC5odG1sDQogIA0KICAgIHJyX2NucyA8LSBjb21vcmJfdGFibGVfd2lkZSAlPiUNCiAgICAgIGZpbHRlcihwb3B1bGF0aW9uID09IHBhc3RlKHBvcCksDQogICAgICAgICAgICAgQ29tb3JiaWRpdHkgPT0gcGFzdGUoaSkpICU+JQ0KICAgICAgbXV0YXRlKGEgPSBDTlNfVG90YWwsICMgbnVtYmVyIENOUyB3L2NvbW9yYg0KICAgICAgICAgICAgIGIgPSBDb21vcmJfVG90YWwgLSBDTlNfVG90YWwsICMgbnVtYmVyIG9mIGNvbW9yYiBwdHMgdy9vIENOUw0KICAgICAgICAgICAgIGMgPSBjbnNfbiAtIENOU19Ub3RhbCwgIyBudW1iZXIgb2YgQ05TIHB0cyB3L28gY29tb3JiDQogICAgICAgICAgICAgZCA9IHN1bShuZXVyb19wdF9jb3VudHNfc3VtKSAtIENvbW9yYl9Ub3RhbCAtIGMsICMgbnVtYmVyIG9mIHB0cyB3L28gY29tb3JiIG9yIENOUw0KICAgICAgICAgICAgIHJpc2tfY25zID0gYS8oYStiKSwNCiAgICAgICAgICAgICByaXNrX25vbl9jbnMgPSBjLyhjK2QpLA0KICAgICAgICAgICAgIHJyX0NOUyA9IHJpc2tfY25zL3Jpc2tfbm9uX2NucywNCiAgICAgICAgICAgICAjIGZvciBjb25maWRlbmNlIGludGVydmFscw0KICAgICAgICAgICAgIGNpXzEgPSAoYi9hKS8oYStiKSwgIyAobjEteDEpL24xIA0KICAgICAgICAgICAgIGNpXzIgPSAoZC9jKS8oYytkKSwgIyAobjIteDIpL24yDQogICAgICAgICAgICAgY2lfcm9vdCA9IHNxcnQoY2lfMSArIGNpXzIpLA0KICAgICAgICAgICAgIGNpX2xvd2VyID0gbG9nKHJyX0NOUykgLSAoMS45NipjaV9yb290KSwNCiAgICAgICAgICAgICBjaV91cHBlciA9IGxvZyhycl9DTlMpICsgKDEuOTYqY2lfcm9vdCksDQogICAgICAgICAgICAgbF9jaV9jbnMgPSBleHAoY2lfbG93ZXIpLA0KICAgICAgICAgICAgIHVfY2lfY25zID0gZXhwKGNpX3VwcGVyKSkgJT4lDQogICAgICBzZWxlY3QoQ29tb3JiaWRpdHksIENvbW9yYl9Ub3RhbCwgTm9uZV9Ub3RhbCwgQ05TX1RvdGFsLCBQTlNfVG90YWwsIHJyX0NOUywgbF9jaV9jbnMsIHVfY2lfY25zKQ0KICANCiAgICAgIHJyX3BucyA8LSBjb21vcmJfdGFibGVfd2lkZSAlPiUNCiAgICAgIGZpbHRlcihwb3B1bGF0aW9uID09IHBhc3RlKHBvcCksDQogICAgICAgICAgICAgQ29tb3JiaWRpdHkgPT0gcGFzdGUoaSkpICU+JQ0KICAgICAgbXV0YXRlKGEgPSBQTlNfVG90YWwsICMgbnVtYmVyIFBOUyB3L2NvbW9yYg0KICAgICAgICAgICAgIGIgPSBDb21vcmJfVG90YWwgLSBQTlNfVG90YWwsICMgbnVtYmVyIG9mIGNvbW9yYiBwdHMgdy9vIFBOUw0KICAgICAgICAgICAgIGMgPSBwbnNfbiAtIFBOU19Ub3RhbCwgIyBudW1iZXIgb2YgUE5TIHB0cyB3L28gY29tb3JiDQogICAgICAgICAgICAgZCA9IHN1bShuZXVyb19wdF9jb3VudHNfc3VtKSAtIENvbW9yYl9Ub3RhbCAtIGMsICMgbnVtYmVyIG9mIHB0cyB3L28gY29tb3JiIG9yIFBOUw0KICAgICAgICAgICAgIHJpc2tfcG5zID0gYS8oYStiKSwNCiAgICAgICAgICAgICByaXNrX25vbl9wbnMgPSBjLyhjK2QpLA0KICAgICAgICAgICAgIHJyX1BOUyA9IHJpc2tfcG5zL3Jpc2tfbm9uX3BucywNCiAgICAgICAgICAgICAjIGZvciBjb25maWRlbmNlIGludGVydmFscw0KICAgICAgICAgICAgIGNpXzEgPSAoYi9hKS8oYStiKSwNCiAgICAgICAgICAgICBjaV8yID0gKGQvYykvKGMrZCksDQogICAgICAgICAgICAgY2lfcm9vdCA9IHNxcnQoY2lfMSArIGNpXzIpLA0KICAgICAgICAgICAgIGNpX2xvd2VyID0gbG9nKHJyX1BOUykgLSAoMS45NipjaV9yb290KSwNCiAgICAgICAgICAgICBjaV91cHBlciA9IGxvZyhycl9QTlMpICsgKDEuOTYqY2lfcm9vdCksDQogICAgICAgICAgICAgbF9jaV9wbnMgPSBleHAoY2lfbG93ZXIpLA0KICAgICAgICAgICAgIHVfY2lfcG5zID0gZXhwKGNpX3VwcGVyKSkgJT4lDQogICAgICBzZWxlY3QoQ29tb3JiaWRpdHksIHJyX1BOUywgbF9jaV9wbnMsIHVfY2lfcG5zKQ0KICANCiAgICAgIHJyIDwtIHJyX2NucyAlPiUNCiAgICAgICAgbGVmdF9qb2luKC4sIHJyX3BucywgYnkgPSBjKCJDb21vcmJpZGl0eSIpKQ0KICANCiAgICBycl9jYWxjc1tbaV1dIDwtIHJyDQogIA0KICB9DQogIA0KICBycl9yZXN1bHRzIDwtIGJpbmRfcm93cyhycl9jYWxjcykgJT4lDQogICAgICBtdXRhdGUocnJfQ05TID0gcm91bmQocnJfQ05TLCAyKSwNCiAgICAgICAgICAgbF9jaV9jbnMgPSByb3VuZChsX2NpX2NucywgMiksDQogICAgICAgICAgIHVfY2lfY25zID0gcm91bmQodV9jaV9jbnMsIDIpLA0KICAgICAgICAgICBycl9QTlMgPSByb3VuZChycl9QTlMsIDIpLA0KICAgICAgICAgICBsX2NpX3BucyA9IHJvdW5kKGxfY2lfcG5zLCAyKSwNCiAgICAgICAgICAgdV9jaV9wbnMgPSByb3VuZCh1X2NpX3BucywgMiksDQogICAgICAgICAgIGBSUiBDTlMgKDk1JSBDSSlgID0gcGFzdGUocnJfQ05TLCAiKCIsIGxfY2lfY25zLCAiLCIsIHVfY2lfY25zLCAiKSIpLA0KICAgICAgICAgICBgUlIgUE5TICg5NSUgQ0kpYCA9IHBhc3RlKHJyX1BOUywgIigiLCBsX2NpX3BucywgIiwiLCB1X2NpX3BucywgIikiKSkgJT4lDQogICAgc2VsZWN0KENvbW9yYmlkaXR5LCBycl9DTlMsIGxfY2lfY25zLCB1X2NpX2NucywgYFJSIENOUyAoOTUlIENJKWAsDQogICAgICAgICAgIHJyX1BOUywgbF9jaV9wbnMsIHVfY2lfcG5zLCBgUlIgUE5TICg5NSUgQ0kpYCkNCiAgDQogICMgdGlkeSB1cCBkYXRhDQogIHdyaXRlLmNzdihycl9yZXN1bHRzICU+JQ0KICAgICAgICAgICAgICBhcnJhbmdlKGRlc2MocnJfQ05TKSkgJT4lDQogICAgICAgICAgICAgIHNlbGVjdChDb21vcmJpZGl0eSwgYFJSIENOUyAoOTUlIENJKWAsIGBSUiBQTlMgKDk1JSBDSSlgKSwgDQogICAgICAgICAgICBwYXN0ZTAoInRhYmxlcy9TdXBwLlRhYmxlNF9SUl8iLCBwb3AsICIuY3N2IiksIHJvdy5uYW1lcyA9IEZBTFNFKQ0KICANCiAgICAjIHJlZm9ybWF0IGRhdGENCiAgcnJfcmVzdWx0c190aWR5X3JyIDwtIHJyX3Jlc3VsdHMgJT4lDQogICAgc2VsZWN0KENvbW9yYmlkaXR5LCBycl9DTlMsIHJyX1BOUykgJT4lDQogICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBycl9DTlM6cnJfUE5TLCBuYW1lc190byA9ICJOZXVybyBTdGF0dXMiLCB2YWx1ZXNfdG8gPSAiUlIiKSAlPiUNCiAgICBtdXRhdGUoYE5ldXJvIFN0YXR1c2AgPSBpZl9lbHNlKGBOZXVybyBTdGF0dXNgID09ICJycl9DTlMiLCAiQ05TIiwgIlBOUyIpKQ0KICANCiAgcnJfcmVzdWx0c190aWR5X2xfY2kgPC0gcnJfcmVzdWx0cyAlPiUNCiAgICBzZWxlY3QoQ29tb3JiaWRpdHksIGxfY2lfY25zLCBsX2NpX3BucykgJT4lDQogICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBsX2NpX2NuczpsX2NpX3BucywgbmFtZXNfdG8gPSAiTmV1cm8gU3RhdHVzIiwgdmFsdWVzX3RvID0gImxfQ0kiKSAlPiUNCiAgICBtdXRhdGUoYE5ldXJvIFN0YXR1c2AgPSBpZl9lbHNlKGBOZXVybyBTdGF0dXNgID09ICJsX2NpX2NucyIsICJDTlMiLCAiUE5TIikpDQogIA0KICBycl9yZXN1bHRzX3RpZHlfdV9jaSA8LSBycl9yZXN1bHRzICU+JQ0KICAgIHNlbGVjdChDb21vcmJpZGl0eSwgdV9jaV9jbnMsIHVfY2lfcG5zKSAlPiUNCiAgICBwaXZvdF9sb25nZXIoY29scyA9IHVfY2lfY25zOnVfY2lfcG5zLCBuYW1lc190byA9ICJOZXVybyBTdGF0dXMiLCB2YWx1ZXNfdG8gPSAidV9DSSIpICU+JQ0KICAgIG11dGF0ZShgTmV1cm8gU3RhdHVzYCA9IGlmX2Vsc2UoYE5ldXJvIFN0YXR1c2AgPT0gInVfY2lfY25zIiwgIkNOUyIsICJQTlMiKSkNCiAgDQogICMgY29tYmluZSBhbGwgdGlkeSByZXN1bHRzDQogIHJyX3Jlc3VsdHNfdGlkeSA8LSBycl9yZXN1bHRzX3RpZHlfcnIgJT4lDQogICAgbGVmdF9qb2luKC4sIHJyX3Jlc3VsdHNfdGlkeV9sX2NpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gYygiQ29tb3JiaWRpdHkiLCAiTmV1cm8gU3RhdHVzIikpICU+JQ0KICAgIGxlZnRfam9pbiguLCBycl9yZXN1bHRzX3RpZHlfdV9jaSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IGMoIkNvbW9yYmlkaXR5IiwgIk5ldXJvIFN0YXR1cyIpKQ0KICANCiAgIyBvcmRlciBieSBDTlMgZGlhZ25vc2VzIHdpdGggaGlnaGVyIFJSDQogIGNuc190b3BfcmVzdWx0cyA8LSBycl9yZXN1bHRzX3RpZHkgJT4lDQogICAgZmlsdGVyKGBOZXVybyBTdGF0dXNgID09ICJDTlMiKSAlPiUNCiAgICBhcnJhbmdlKFJSKSAlPiUNCiAgICBzZWxlY3QoQ29tb3JiaWRpdHkpDQogIA0KICBycl9yZXN1bHRzX3RpZHkkQ29tb3JiaWRpdHkgPC0gIG9yZGVyZWQocnJfcmVzdWx0c190aWR5JENvbW9yYmlkaXR5LCBsZXZlbHMgPSBjbnNfdG9wX3Jlc3VsdHMkQ29tb3JiaWRpdHkpDQogIA0KICBjbnNfcnJfcGxvdCA8LSBnZ3Bsb3QocnJfcmVzdWx0c190aWR5ICU+JQ0KICAgICAgICAgICBmaWx0ZXIoYE5ldXJvIFN0YXR1c2AgPT0gIkNOUyIpLA0KICAgICAgICAgYWVzKHggPSBSUiwgeSA9IENvbW9yYmlkaXR5KSkgKw0KICAgIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSAxKSwgc2l6ZSA9IC4yNSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKw0KICAgICAgZ2VvbV9lcnJvcmJhcmgoYWVzKHhtYXggPSB1X0NJLCB4bWluID0gbF9DSSksIHNpemUgPSAwLjcsIGhlaWdodCA9IDAuMiwgY29sb3IgPSAiZ3JheTUwIikgKw0KICAgICAgZ2VvbV9wb2ludChzaXplID0gMS41LCBjb2xvciA9ICJ0b21hdG8iKSArDQogICAgICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDQsIDEpLCBsYWJlbHMgPSBzZXEoMCwgNCwgMSksDQogICAgICAgICAgICAgICAgICAgICAgICAgbGltaXRzID0gYygwLDQpKSArDQogICAgeWxhYigiIikgKw0KICAgIHhsYWIoIlJlbGF0aXZlIFJpc2siKSArDQogICAgZ2d0aXRsZSgiQ05TIFBhdGllbnRzIikgKyANCiAgICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiKSkNCiAgDQogICMgYXJyYW5nZSBieSB0b3AgUE5TIHJlc3VsdHMNCiAgcG5zX3RvcF9yZXN1bHRzIDwtIHJyX3Jlc3VsdHNfdGlkeSAlPiUNCiAgICBmaWx0ZXIoYE5ldXJvIFN0YXR1c2AgPT0gIlBOUyIpICU+JQ0KICAgIGFycmFuZ2UoUlIpICU+JQ0KICAgIHNlbGVjdChDb21vcmJpZGl0eSkNCiAgDQogIHJyX3Jlc3VsdHNfdGlkeSRDb21vcmJpZGl0eSA8LSAgb3JkZXJlZChycl9yZXN1bHRzX3RpZHkkQ29tb3JiaWRpdHksIGxldmVscyA9IHBuc190b3BfcmVzdWx0cyRDb21vcmJpZGl0eSkNCiAgDQogIA0KICBwbnNfcnJfcGxvdCA8LSBnZ3Bsb3QocnJfcmVzdWx0c190aWR5ICU+JQ0KICAgICAgICAgICBmaWx0ZXIoYE5ldXJvIFN0YXR1c2AgPT0gIlBOUyIpLA0KICAgICAgICAgYWVzKHggPSBSUiwgeSA9IENvbW9yYmlkaXR5KSkgKw0KICAgIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSAxKSwgc2l6ZSA9IC4yNSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKw0KICAgICAgZ2VvbV9lcnJvcmJhcmgoYWVzKHhtYXggPSB1X0NJLCB4bWluID0gbF9DSSksIHNpemUgPSAwLjcwLCBoZWlnaHQgPSAwLjIsIGNvbG9yID0gImdyYXk1MCIpICsNCiAgICAgIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSwgY29sb3IgPSAic2xhdGVibHVlIikgKw0KICAgICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxLjUsIDAuNSksIGxhYmVscyA9IHNlcSgwLCAxLjUsIDAuNSksDQogICAgICAgICAgICAgICAgICAgICAgICAgbGltaXRzID0gYygwLDEuNSkpICsNCiAgICB5bGFiKCIiKSArDQogICAgeGxhYigiUmVsYXRpdmUgUmlzayIpICsNCiAgICBnZ3RpdGxlKCJQTlMgUGF0aWVudHMiKSArIA0KICAgIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIpKQ0KICANCiAgcG5zX3JyX3Bsb3QgPC0gc2V0X3BhbmVsX3NpemUocG5zX3JyX3Bsb3QsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgd2lkdGggID0gdW5pdCg3LCAiY20iKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBoZWlnaHQgPSB1bml0KDYuNSwgImluIikpDQogIA0KICBjbnNfcnJfcGxvdCA8LSBzZXRfcGFuZWxfc2l6ZShjbnNfcnJfcGxvdCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB3aWR0aCAgPSB1bml0KDcsICJjbSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhlaWdodCA9IHVuaXQoNi41LCAiaW4iKSkNCiAgDQogICNncmlkLmFycmFuZ2UoY25zX3JyX3Bsb3QsIHBuc19ycl9wbG90LCBuY29sPTIpDQogIA0KICBnZ3NhdmUocGFzdGUwKCJmaWd1cmVzL0ZpZ3VyZTQucmVsYXRpdmVfcmlza18iLCBwb3AsICIucG5nIiksIGdyaWQuYXJyYW5nZShjbnNfcnJfcGxvdCwgcG5zX3JyX3Bsb3QsIG5jb2w9MiksIHdpZHRoID0gMTUsIGhlaWdodCA9IDgsIGRwaSA9IDMwMCkNCiAgDQogIH0NCmBgYA0KDQpgYGB7ciBldmFsPVRSVUUsIGluY2x1ZGU9RkFMU0V9DQpycl9hZHVsdCA8LSBjYWxjdWxhdGVfcnIoaXNfcGVkaWF0cmljID0gRkFMU0UpDQojcnJfcGVkaWF0cmljIDwtIGNhbGN1bGF0ZV9ycihpc19wZWRpYXRyaWMgPSBUUlVFKQ0KYGBgDQoNCiFbXShmaWd1cmVzL0ZpZ3VyZTQucmVsYXRpdmVfcmlza19BZHVsdC5wbmcpeyNpZCAuY2xhc3Mgd2lkdGg9MTAwMCBoZWlnaHQ9ODAwcHh9DQoNCg0KIyMjIExQQ0EgZGV2aWFuY2UgZXhwbGFpbmVkDQoNCkhvdyBtdWNoIGRldmlhbmNlIGlzIGV4cGxhaW5lZCB3aXRoIDEwIHByaW5jaXBhbCBjb21wb25lbnRzIGZvciAzMCBjb21vcmJpZGl0eSB0eXBlcz8NCg0KQWNyb3NzIGFsbCBoZWFsdGhjYXJlIHN5c3RlbXMsIDEwIHByaW5jaXBhbCBjb21wb25lbnRzIGV4cGxhaW5lZCBhYm92ZSA3NSUgb2YgdGhlIGRldmlhbmNlLg0KDQpgYGB7cn0NCmNvbXBhcmVfZGV2aWFuY2UgPC0gZnVuY3Rpb24oc29ydGVkX3NpdGVzLCBpc19wZWRpYXRyaWM9RkFMU0UpIHsNCiAgDQogIHBjYV9kZXYgPC0gbGlzdCgpDQogIA0KICBpZihpc19wZWRpYXRyaWM9PUZBTFNFKSB7DQogICAgcG9wdWxhdGlvbiA9ICJhZHVsdHMiDQogICAgc29ydGVkX3NpdGVzID0gc29ydGVkX3NpdGVzWyFzb3J0ZWRfc2l0ZXMlaW4lIGMoIkdPU0hfcmVzdWx0cyIsICJCQ0hfcmVzdWx0cyIpXQ0KICAgIH0gZWxzZSB7DQogICAgcG9wdWxhdGlvbiA9ICJwZWRpYXRyaWNzIg0KICAgIH0NCg0KICAjIGZvciBlYWNoIGhlYWx0aGNhcmUgc3lzdGVtLCB3ZSB3aWxsIGNhbGN1bGF0ZSB0aGUgdG90YWwgbnVtYmVyIG9mIHBhdGllbnRzIHdpdGggZWFjaCBJQ0QgY29kZQ0KICBmb3IgKGkgaW4gc29ydGVkX3NpdGVzKSB7DQogICAgcHJpbnQoaSkNCiAgICB0bXAgPC0gZ2V0KGkpDQogICAgdG1wX3BjYSA8LSB0bXBbW2MoDQogICAgICAiY29tb3JiaWRpdGllcyIsDQogICAgICBwYXN0ZTAoImNvbW9yYl8iLCBwb3B1bGF0aW9uKSwNCiAgICAgICJkZXZpYW5jZV9leHBsIg0KICAgICldXSAlPiUgDQogICAgICBkYXRhLmZyYW1lKCkgJT4lIA0KICAgICAgcmVuYW1lKGRldl9leHBsID0gJy4nKQ0KICAgIHRtcF9wY2Ekc2l0ZSA8LSB0bXBbWyJzaXRlIl1dDQogICAgcGNhX2RldltbaV1dIDwtIHRtcF9wY2ENCiAgICANCiAgfQ0KICANCiAgcGNhX2RmIDwtIGJpbmRfcm93cyhwY2FfZGV2KQ0KICANCiAgcmV0dXJuKHBjYV9kZikNCiAgDQp9DQoNCnBjYV9hZHVsdCA8LSBjb21wYXJlX2RldmlhbmNlKHNvcnRlZF9zaXRlcyA9IGFkdWx0X3NpdGVzLCBpc19wZWRpYXRyaWMgPSBGQUxTRSkNCg0KDQpwY2FfZGF0YSA8LSBiaW5kX3Jvd3MocGNhX2FkdWx0KQ0KDQoNCnRoZW1lX3NldCh0aGVtZV9jbGFzc2ljKCkpDQoNCnBjYV9wbG90IDwtIGdncGxvdChwY2FfZGF0YSwgDQogICAgICAgYWVzKHggPSBmY3RfcmVvcmRlcihzaXRlLCAtZGV2X2V4cGwpLCB5ID0gZGV2X2V4cGwsIGdyb3VwID0gMSkpICsNCiAgZ2VvbV9wb2ludChzaXplID0gMi41KSArDQogIGdlb21fbGluZSgpICsNCiAgeGxhYigiSGVhbHRoY2FyZSBTeXN0ZW0iKSArIA0KICB5bGFiKCJQcm9wb3J0aW9uIG9mIERldmlhbmNlIGV4cGxhaW5lZCIpICsgIA0KICB5bGltKDAsMSkgKyANCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTkwLCBoanVzdD0xLCBmYWNlID0gImJvbGQiLCBzaXplID0gMTIpLA0KICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiksIA0KICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDE1KSk7IHBjYV9wbG90DQoNCmdnc2F2ZSgiZmlndXJlcy9TdXBwRmlnX3BjYV9kZXZpYW5jZS5wbmciLCBwY2FfcGxvdCwgaGVpZ2h0ID0gNiwgd2lkdGggPSA2KQ0KYGBgDQo=